Files
UnrealEngineUWP/Engine/Source/Runtime/NavigationSystem/Private/NavigationSystem.cpp
jason bestimt 3d121a822b #CodeReview: jason.bestimt,nick.whiting,mike.beach,ryan.vance
Merge conflict.
jason.bestimt, please merge this change by hand.
More info at https://robomerge.epicgames.net#DEVVR

















































































































































































































































































































--------------------------------------
Copying //UE4/Release-Staging-4.21 to //UE4/Dev-Main (Source: //UE4/Release-4.21 [at] 4447256)

============================
  MAJOR FEATURES & CHANGES
============================

Change 4447256 by Joe.Graf

	Added a video settings field to the debug menu so you can see your choice

	#jira: UE-65026, UE-65017

Change 4446451 by Matt.Kuhlenschmidt

	Fix inline edit conditions not supporting undo

	PR #4854: UE-61432: Add FScopedTransaction to SetEditConditionState (Contributed by projectgheist)


	#jira UE-61432

Change 4446389 by Mieszko.Zielinski

	Fixed navmesh tiles AABB being calculated wrong for large agents #UE4

	#jira UE-65038

Change 4446171 by Matt.Kuhlenschmidt

	Fix crash resetting cascade distributions to default

	#jira UE-64458

Change 4446143 by Uriel.Doyon

	Re-worked fix from CL 4442729 to handle !SUPPORTS_INDEPENDENT_SAMPLERS

	#jira UE-64695

Change 4446071 by Jeff.Fisher

	Duplicating for 4.21 Change: 4446057 UE-64942 Material Switching in MRMesh
	-Fixed run time material switching on MRMesh
	#jira UE-64942

	Files:
	//UE4/Dev-VR/Engine/Source/Runtime/MRMesh/Private/MRMeshComponent.cpp#24
	//UE4/Dev-VR/Engine/Source/Runtime/MRMesh/Public/MRMeshComponent.h#16

Change 4445939 by Joe.Conley

	#jira UE-54503 - Transparency for textures applied to the spectator screen for VR

	Added a flag bUseAlpha that is checked in FDefaultSpectatorScreenController::RenderSpectatorModeMirrorAndTexture(), if (SpectatorScreenModeTexturePlusEyeLayout_RenderThread.bDrawEyeFirst).  It can be set with the SetSpectatorScreenModeTexturePlusEyeLayout Blueprint node.

Change 4445917 by Matt.Kuhlenschmidt

	Guarad against crash when destroying asset view items

	#jira UE-61108

Change 4445911 by Matt.Kuhlenschmidt

	Guard against null objects in asset editors

	#jira UE-64499

Change 4445574 by Wyeth.Johnson

	Kill volume cleanup, helper function for cone, edge case on pendulum
	#jira none

Change 4445472 by Matt.Collins

	UE-64680

	The new depth bounds optimization in PostAO doesn't work with MSAA. The AO target and depth buffer are different sample counts. Forcing this feature off for now.
	In the future if we want to turn it back on we will either have to resolve the depth buffer or increase the AO target sample count.

	#jira UE-64680

Change 4444917 by Bogdan.Vasilache

	UE-63963 - Fix FSystemTextures.InitializeTextures initialization ordering
	#jira UE-63963

Change 4444779 by andrew.porter

	QAGane: Updating focus so we can select UMG test button in TM-PlaybackControls

	#jira UE-29618

Change 4444678 by Richard.Wallis

	Clone of Dev-Rendering CL 4444343:

	Fix for eyedropper in the Color Picker window not grabbing any color values on Mac.  Bug also says Linux - unable to verify if its the same issue so kept change to Mac specific code.

	#jira UE-53789

Change 4444677 by Lauren.Ridge

	Context menu opening cancels a rename
	#jira UE-64525

Change 4444321 by Michael.Dupuis

	#jira none: uncomment the lazy obj ptr warning as the integration is completed

Change 4444288 by Simon.Tourangeau

	BP_SunPosition now has a soft ref to BP_SkySphere to prevent cross level referencing

	#jira UE-64607

Change 4444287 by JeanMichel.Dignard

	Copied 4442004 Daniel.Coelho

	Fix for being unable to trigger on variants from UMG blueprints

	#jira UE-64948

Change 4444105 by Mitchell.Wilson

	Saving assets to resolve UMeshDescription warnings
	#jira UE-64022

Change 4444080 by Ben.Marsh

	Fix path to Win64 DLLs when building DDC on Linux.

	#jira

Change 4444036 by Patrick.Boutot

	Fix crash when there are too many pending task in ImageWriteQueue.
	#jira UE-64991

Change 4443920 by Ben.Marsh

	Fix runtime dependencies not being enumerated for modules that aren't linked into a binary.

	#jira UE-65024

Change 4443889 by Ben.Marsh

	Copy Windows DDCUtils DLLs when building DDC for Linux on the Win64 host platform.

	#jira UE-64975

Change 4443882 by Ben.Marsh

	Fix CIS compile error in UE4Game HTML5.

	#jira

Change 4443531 by Nick.Shin

	#jira UE-65015  HTML5: Project Crashes During Launching Engine due to "Texture format 'R8G8B8A8_UINT' not supported"

	this solution was based on: UE-48208

Change 4443358 by Joe.Graf

	Changed the world mapping state conversion in ARKit to return NotAvailable, Mapping, Mapped

	#jira: UE-65023

Change 4443217 by Dan.Oconnor

	Fixed crash after hitting stop when in-stack-debugging
	Clear GPlayInEditorID when stopping PIE at a blueprint exception/breakpoint

	#jira UE-64895, UE-65005, UE-65008

Change 4443200 by Max.Chen

	Sequencer: Now accouting for frame rate when copying key tangents when converting from matinee to level sequence.

	#jira UE-65020

Change 4443062 by Dan.Oconnor

	Fixed crash when undoing after deleting a function graph that has a math expression node

	#jira UE-62134

Change 4442903 by Lauren.Ridge

	Allowing material graph nodes to have duplicate names in all cases except creating a new parameter from a constant
	#jira UE-64421

Change 4442878 by Joe.Graf

	Disabled the clion ignore file generation on Windows since it isn't supported correctly there

	#jira: UE-64871

Change 4442840 by Max.Chen

	Control Rig: Fix crash tearing down sequencer object spawner

	#jira UE-65013

Change 4442810 by Ethan.Geller

	Fix build for SoundVisualizations plugin: add const qualifiers for WaveInfo. #jira none #rb none #fyi aaron.mcleran

Change 4442769 by Brandon.Schaefer

	Linux: Use the size of our In Rect which is the size of our buffer rather then the size of the Texture which is not always the same size as the buffer

	#jira UE-64785
[at]Rolando.Caloca, [at]Arciel.Rekman

Change 4442758 by Dan.Oconnor

	"Assign [Delegate Name]" blueprint context menu entries now properly create a custom event node again
	#jira UE-65000

Change 4442729 by Uriel.Doyon

	Changed GetForwardDynamicShadowFactors implementation to use Texture2DSampleLevel instead of Load.
	This is because we sometime bind the GWhiteDummy when the light attenuation buffer is not required, which is a 1x1 texture.

	#jira UE-64695

Change 4442724 by Max.Chen

	Sequencer: Added back in ScrubHandleUp/Down for the VisualLoggerTimeSliderController to fix missing brush warning

	#jira UE-64994

Change 4442519 by Marc.Audy

	Avoid broadcasting events that could result in invalidating the array we're iterating.
	#jira UE-64959

Change 4442460 by JeanMichel.Dignard

	Fixed crash with VRED importer
	- DataTable wasn't counting its changes properly resulting in a failed check.

	#jira UE-64982

Change 4442216 by Ethan.Geller

	Unshelved from pending changelist '4438353':

	Ensure const correctness in USoundWave cook processing.

Change 4442191 by Mitchell.Wilson

	Updating min ios version to resolve warnings.
	#jira UE-64815

Change 4442124 by Arciel.Rekman

	Another fix for lock up on starting without audio devices (UE-64506).

	#jira UE-64506

Change 4442006 by Ben.Marsh

	Fix debug info being included for Lumin binaries on Mac.

	#jira UE-65002

Change 4441939 by Ben.Marsh

	Fix inability to package Mac targets from Windows.

	#jira UE-64148

Change 4441374 by tim.gautier

	QAGame: Resaved Niagara assets.
	Rebuilt lighting / resaved TM-ShaderModels
	#jira UE-29618

Change 4441369 by Rolando.Caloca

	UE4.21 - hlslcc - Fix UAV getting removed

	#jira

Change 4441347 by Michael.Dupuis

	#jira UE-63631: LOD Distribution settings, must be > 1.01 as otherwise 1/1 will not give correct result.

Change 4441132 by Dan.Oconnor

	Remove unused dtor

	#jira None

Change 4441108 by Dan.Oconnor

	Fix leaking entries for PLACEHOLDER classes in the new ClassToPlaceholderMap

	#jira UE-64971

Change 4441096 by tim.gautier

	Adding the VRMode_Ball asset for VRMode testing.
	#jira UE-29618

Change 4441093 by Ryan.Gerleve

	Fix the writer's size issue in the auth handler when requesting resends. This fixes packet over/underflows. Also make it so that dropall drops all of auth's packets as well.

	#Jira UE-63796, UE-64156

	#AUTOMERGE using branch //UE4/Dev-Networking_to_//UE4/Release-4.21 of change#4374419 by Jake.Leonard on 2018/09/18 15:41:48.

Change 4441065 by Ryan.Gerleve

	Fix using SetIp(uint) on an IPv6 only platform so that both cases are handled properly. Should contain the same flow as the IPv6 framework.

	#Jira UE-64129

	#AUTOMERGE using branch //UE4/Dev-Networking_to_//UE4/Release-4.21 of change#4374299 by Jake.Leonard on 2018/09/18 15:23:37.

Change 4441052 by Ben.Zeigler

	#jira UE-64446 Fix crash on cooked devices when using GameplayCues but not explicitly setting a manager
	It is unsafe to reference dynamic objects from a CDO so spawn an instance of the default class

Change 4441042 by Ryan.Gerleve

	Remove the bForceUDP from the new CreateSocket signature. This is an argument that came in from UE3 and is not supported. It should not continue to exist anymore.

	#Jira: UE-63879

	#AUTOMERGE using branch //UE4/Dev-Networking_to_//UE4/Release-4.21 of change#4356623 by Jake.Leonard on 2018/09/10 20:35:09.

Change 4441005 by Ben.Zeigler

	#jira UE-64446 Call InitGlobalData from ActionRPG sample and set up path for cue notifies. All projects using abilities should do this

Change 4440944 by Ryan.Gerleve

	Because addrconfig is not supported on HTML5. This is not documented anywhere, so any query with the flag will just be removed from any queries.

	#Jira UE-63791

	#AUTOMERGE using branch //UE4/Dev-Networking_to_//UE4/Release-4.21 of change#4356538 by Jake.Leonard on 2018/09/10 19:35:29.

Change 4440912 by Ryan.Gerleve

	Skip lobbies we are already a part of when generating matchmaking results. This allows us to not try to fetch data on ourselves.

	#Jira UE-63543

	#AUTOMERGE using branch //UE4/Dev-Networking_to_//UE4/Release-4.21 of change#4356532 by Jake.Leonard on 2018/09/10 19:34:39.

Change 4440861 by Ben.Zeigler

	#jira UE-61890 Fix XGE shader compilation on newer incredibuilds by checking registry for path

Change 4440850 by Michael.Dupuis

	#jira UE-62426: Reset InstanceCountToRender if the static mesh is valid or we have no instances

Change 4440514 by Ben.Marsh

	Add additional info to comment around optimization being disabled due to codegen bug.

	#jira

Change 4440501 by Ben.Zeigler

	#jira UE-64971 Fix resolving export tracker to clean itself up after resolving. This fixes the immediate crash, but there may be another way it can end up crashing so this needs further investigation as the structure is unsafe

Change 4440479 by Rex.Hill

	Fix debug hud not rendering when swapping between PIE/SIE modes
	#jira UE-64704

Change 4440447 by Joe.Graf

	Rename/move AR samples

	#jira: UE-64944

Change 4440338 by Joe.Graf

	Cleaned up leftover files from the bad 4.20->Dev-VR merge

	#jira: UE-64957

Change 4440318 by Joe.Graf

	Fixed the bad asset merge that happened from 4.20 back to Dev-VR

	#jira: UE-64957

Change 4440237 by Ben.Zeigler

	#jira UE-64960 Disable validating data dialog when saving, and ony show if > 0.1 seconds

Change 4440157 by Mieszko.Zielinski

	Fixed a bug in how UNavModifierComponent handles FKConvexElems #UE4

	#jira UE-60987

Change 4440017 by Mitchell.Wilson

	Saving SunlightColorLUT in KiteDemo to resolve warnings.
	#jira UE-64744

Change 4439856 by Jurre.deBaare

	Crash when trying to "Update Mesh Section" on any procedural mesh just after "Clear Mesh Section" and "Create Mesh Section" for different vertices and triangles.
	#fix Ensure that we recreate the scene proxy whener a mesh section is updated with a larger amount of vertices than original, this will reinit the vertex buffers to the correct sizes
	#jira UE-60607

Change 4439831 by Ben.Marsh

	Fix ClangEditor CIS warning.

	Change MSVC_PRAGMA to not be defined when compiling using Clang on Windows. Also add a link to bad codegen report on Microsoft's developer forum for an instance of optimization being disabled.

	#jira

Change 4439828 by mason.seay

	Added variable to blueprint

	#jira UE-29618

Change 4439763 by Jurre.deBaare

	#jira UE-62048
	PR #4892: HLOD System: Fix crash when static mesh has no valid material (Contributed by user37337)
	#fix slightly modifier fix

Change 4439731 by Joe.Graf

	Rename/move AR samples

	#jira: UE-64944

Change 4439715 by Cosmin.Sulea

	UE-64937 - IphonePackager cannot verify UDID on new devices (XS or newer)
	#jira UE-64937

Change 4439697 by Wyeth.Johnson

	Metadata errors and fixes
	#jira none

Change 4439658 by Michael.Dupuis

	#jira UE-63831: Added some guard to prevent the crash

Change 4439605 by Michael.Dupuis

	#jira UE-53944: Added some asserto help track what could be happening in this case

Change 4439516 by Ben.Marsh

	UBT: Set the permissions on directories containing scripts to be executed as part of remote builds.

	#jira UE-64860

Change 4439395 by Michael.Dupuis

	#jira UE-63637: When doing migration from 4.18, if the data was in a BP, use a Reset instead of a Reserve, as otherwise we will end up with twice the amounf of instances.

Change 4439197 by Michael.Dupuis

	#jira UE-63242
	#jira UE-63123:
	Integrate from Dev-Editor branch

Change 4439162 by Mieszko.Zielinski

	Made sure NavigationSystemConfig respects GEngine->NavigationSystemClassName #UE4

	#jira UE-62991

Change 4439106 by Cosmin.Sulea

	UE-64603 - Android Devices do not show up in device list for Device Output Log
	#jira UE-64603

Change 4438283 by Ben.Zeigler

	#jira UE-63825 Integrate pull request:
	PR #5073: Implement LoadGameFromMemory function. (Contributed by KelbyG)

Change 4438242 by Ben.Zeigler

	Test case for #jira UE-63705. To verify, open WeakReferencedPrimaryAsset and note that the UntrackedMeshPtr is set to AT_StaticMesh.
	Then, right click and show references, that mesh should NOT show up, as it is marked as Untracked via metadata

Change 4438238 by Ben.Zeigler

	#jira UE-63843 Temporary fix for Visual Studio 2017 compile bug causing crashes in Audio Mixer code

Change 4438237 by Ben.Zeigler

	#jira UE-64770 Fix bug where AssetManager could fail to call delegates in sync load mode

Change 4438236 by Ben.Zeigler

	#jira UE-64466 Fix issues with debug builds with ENABLE_NAN_DIAGNOSTIC and TransformNonVectorized, it's not safe to use the FVector constructor as it will always ensure. Also unified checks with the vectorized version to be consistent

Change 4438234 by Ben.Zeigler

	#jira UE-63705 Add full support for Untracked soft object references, enable by adding that tag to the metadata for a property or using FSoftObjectPathSerializationScope with NeverCollect
	If set the references will not be automatically cooked, reported to the asset registry, or cause warnings when deleting a referenced actor
	They are meant to be used for cases where they are just used as a lazy cache, such as things converted from FLazyObjectPtr
	The reference from Foliage to it's base actor is now an Untracked SoftObjectPtr instead of a LazyPtr

Change 4437969 by Michael.Lentine

	Fix gpu skin cache with multiple cloth sections.

	#jira ue-62951

Change 4437907 by Joe.Graf

	Rename/move AR samples

	#jira: UE-64944

Change 4437901 by Mikey.Boyd

	Resaved QA-Promotion and TM-ShaderModels to clear up warnings for Linear Color Curves in UE-64949.

	#jira UE-29618

Change 4437877 by Dan.Oconnor

	Guard against double-queuing in Macro BP gather phase

	#jira None

Change 4437615 by Sorin.Gradinaru

	UE-64624 App fails to open when packaged for Distribution - Android
	#jira UE-64624
	#Android
	#4.21

	As Chris.Babcock mentioned, this is caused by an app pause event triggered when the obb downloader starts and puts the activity in background. Adding a flag to ignore the first app pause in this situation.

Change 4437541 by Andrew.Porter

	QAGame: Updating location of an animation so it can be visiblty in the sequence

	#jira UE-29618

Change 4437078 by Mitchell.Wilson

	Updating lerp tooltip based on updates by Sam Deiter.
	#jira UEDOC-8219

Change 4436943 by Bogdan.Vasilache

	UE-61698 - Android + Mouse: Hover events and Get Mouse Position on Viewport not working
	#jira UE-61698

Change 4436915 by Dan.Oconnor

	Remove debugging hook accidentally added in 4235135

	#jira None

Change 4436204 by Joe.Graf

	Switched the AREnvProbe sample to Metal 2.1 to fix the z-fighting that happens in 2.0 and below

	#jira: UE-64932

Change 4435856 by Arciel.Rekman

	Fix CIS (UE-64925).

	#jira UE-64925

Change 4435811 by Mark.Satterthwaite

	Sort out the way we report R11G11B10 support for Metal so that it should work correctly on iOS & tvOS.

	#jira UE-64905

Change 4435783 by Brandon.Schaefer

	libvpx: Rebuild on centos7

	#jira UE-64887

Change 4435710 by Thomas.Sarkanen

	Fixed crash drag-dropping a variable onto a state machine

	#jira UE-64539 - [CrashReport] UE4Editor-CoreUObject.dylib!CastLogError()

Change 4435694 by Andrew.Grant

	Removed some stale Orion tests

	#jira UE-64857

Change 4435662 by Ben.Marsh

	Rework the way that custom icons are used on Windows.

	* For code projects, the custom icon is embedded into the resource when compiled from UBT. This ensures the custom icon is set even before packaging, and removes the need for a deployment step.
	* For content projects, the custom icon is set after packaging using the previous method.

	#jira UE-64527

Change 4435599 by Michael.Lentine

	Avoid crash with null materials

	#jira ue-64819

Change 4435596 by Michael.Lentine

	Account for the case where skeletal mesh doesn't exsit in the component body setup.

	#jira ue-63104

Change 4435563 by Brandon.Schaefer

	SDL2: Fix pointer barriers on resize/move and multimonitor

	Part of this change is from PR 5041 (thanks yaakuro)

	#jira UE-60461, UE-63372

Change 4435557 by Michael.Lentine

	Allow for non uniform scaling in some of the capsule functions.

	#jira ue-59651

Change 4435518 by Mitchell.Wilson

	Saving two assets to resolve empty engine version warnings in RemoteSessionApp
	#jira UE-64747

Change 4435416 by Robert.Manuszewski

	Fixing a Cook On The Fly server crash when an asset that has been requested by the client is missing from the server

	#jira UE-64616

Change 4434780 by Uriel.Doyon

	Fixed Niagara proxy rendering in ES2 while the shaders are not available.

	#jira UE-64666

Change 4434758 by Joe.Graf

	Fixed the use of deprecated NSKeyedArchiver and NSKeyedUnarchiver methods

	#jira: UE-64913

Change 4434757 by Joe.Graf

	Removed invalid plugins that were specified for the AR samples

	#jira: UE-64914

Change 4434497 by Joe.Graf

	Fixed incomplete merge of ARSharedWorld sample

	#jira:  UE-64673

Change 4434281 by Joe.Conley

	#jira UE-63793 - ARKit Crash with Session Type: Image

	Removing error logging since it's also getting logged higher up in the callstack.

Change 4434250 by Joe.Conley

	#jira UE-63793 - ARKit Crash with Session Type: Image

	Instead of an assert (and thus crash) if Session Configuration is null (which happens if device/os doesn't support ARKit 2.0), just print out an error to the log and keep running.

Change 4434173 by Aaron.McLeran

	Fix for audio device shutdown

	-- Actually, need to make sure the flush code doesn't execute on platforms without threads. This will still fail on HTML5

	#jira UE-64619

Change 4434112 by Aaron.McLeran

	Bug fix pausability

	- Pausing sound while stopping can cause a crash when updating the sound

	#jira none

Change 4433986 by Michael.Lentine

	Avoid dereferencing null.

	#jira ue-63593

Change 4433954 by Michael.Lentine

	Use correct enable flag.

	#jira ue-64801

Change 4433867 by Uriel.Doyon

	Fixed integration issue from Niagara with "Collision Query" module

	#jira UE-64906

Change 4433779 by Dan.Oconnor

	Mirror 4379405 from Dev-Framework
	Add flags to track bMarkedObjectsPendingKill and bCleanedUpWorld to ensure that sublevels are cleaned up properly

	#jira UE-64227

Change 4433771 by Dan.Oconnor

	Mirror 4365800 from Dev-Framework
	Avoid marking a blueprint as modified when the compiler renames a graph

	#jira UE-63942

Change 4433763 by Dan.Oconnor

	Mirror 4363197 from Dev-Framework
	Perform a placeholder resolve pass immediately after the CDO has been serialized

	#jira UE-62928

Change 4433716 by Chris.Bunner

	#author Bogdan.Vasilache
	Comitting shelf 4433043 for UE-64237.

	#jira UE-64237

Change 4433609 by Uriel.Doyon

	Fixed how UNiagaraDataInterfaceCurlNoise release its render resource by adding a release command in BeginDestroy.

	#jira UE-64898

Change 4433524 by zak.parrish

	Final functionality for AR SaveLoad now in place. Just needs cleanup/commenting and About screen
	#JIRA UE-64681

Change 4433512 by Christina.TempelaarL

	Mirror 4373989 from Dev-Editor.
	Cascade: Copy particle emitter properties when invoking Duplicate Emitter or Export Emitter in the Cascade editor.
	#jira UE-63856

Change 4433328 by Chris.Babcock

	Fix ConfigRules.jar parsing for Linux line endings

	#jira UE-64892
	#ue4
	#android

Change 4433279 by Zachary.Wilson

	Switching DoF methods for PostProcess volumes in QA_PostProcessing and TM_Showflags_LightingFeatures.
	#jira UE-64659

Change 4433092 by Dan.Oconnor

	Fix missing include

	#jira None

Change 4433046 by Michael.Lentine

	Renamed command to relfect actual behavior.

	#jira ue-55368

Change 4433024 by Ben.Marsh

	PR #5154: Prevent crunch_x64.exe from showing in Git commit changes (Contributed by projectgheist)


	#jira

Change 4432990 by Wyeth.Johnson

	Fixing issues related to particle mass and gravity
	#jira none

Change 4432979 by Uriel.Doyon

	Fixed DOF when enabling Pre-Exposure
	Added units to the intensity property of point, spot, rect, directional and sky lights.
	Refactored the VisualizeHDR so that it shows the HDR view is a PIP on top of the main render.
	Fixed PixelInspector results being affected by pre-exposure.
	Added a pre-exposure output value to the PixelInspector.
	Moved ExposureCompensation to the Exposure tab in post-process settings.
	Changed max slider intensity for directional light to be in 120 000 lux
	Changed max slider intensity for sky light to be in 50 000 cd/m2
	Non bUseInverseSquaredFalloff point lights now have their units forced to ELightUnits::Unitless
	Moved light units to advanced display, now that the unit is embeded in the Intensity property
	Added Candela, Lux and CandelaPerMeterSquared units to the engine units
	Fixed meta data properties to support the "Units" keyword.

	#jira UE-64881

Change 4432953 by Chris.Gagnon

	CL 4356067 copied to Release-4.21
	#jira UE-63861

Change 4432934 by Chris.Gagnon

	CL 4399650 copied to Release-4.21
	#jira UE-64070

Change 4432913 by Dan.Oconnor

	Mirror 4363175 from Dev-Framework
	Reuse subobjects during archetype reinstancing path

	#jira UE-59135

Change 4432901 by Michael.Lentine

	Adding missing counter.

	#jira ue-59969

Change 4432895 by Dan.Oconnor

	Mirror 4344890 from Dev-Framework
	Make FObjectFinder visibile to reference collectors and properly replace subobjects on the CDO. This fixes instability after reloading the FirstPersonCharacter blueprint
	#jira UE-54419, UE-63061

Change 4432805 by Uriel.Doyon

	Fixed error when compiling some Niagara GPU particles
	#jira UE-64885

Change 4432789 by Michael.Lentine

	Fix comments.

	#jira 58064

Change 4432766 by Ben.Marsh

	Fix issues with mismatched solution/project configurations.

	* Every C++ project now has an "invalid" configuration. Trying to build any solution configuration which does not apply to the project will now build that, which prints an error rather than building a completely different configuration.
	* Available configurations are now parsed from C# files, so the "Release" configuration will now be used if "Development" is not available, and platforms are supported correctly.
	* Programs are now built in game and editor configurations.

	#jira UE-59288

Change 4432649 by Michael.Lentine

	Resize Temp instead of Out.

	#jira ue-62919

Change 4432540 by Chris.Bunner

	[Duplicating 4432262] - HLOD visibility state is now cleared when not in use.
	#jira UE-64432

Change 4431926 by Chad.Garyet

	Integrating changes from dev-build over to release-4.21
	- Add win64 host support to DDC Group Linux
	- Change project params split to remove empty entries
	- Change BuildDerivedDataCache to skip building a feature pack if there are no project specific platforms for it out of the available targetplatforms
	#jira UE-62311

Change 4431602 by Simon.Therriault

	Merging MediaBundle fix

	#jira UE-64853

Change 4431481 by Brandon.Schaefer

	Linux: Break grabs and bring down pointer barreirs when we have crashed

	#jira UE-63981

[at]Arciel.Rekman

Change 4431456 by Jules.Blok

	Merging CL 4428447 from Dev-VR - Update the WMR plugin for Dev-Rendering changes.

	#jira UE-64507

Change 4431310 by Robert.Manuszewski

	Fixed a crash when setting the initial size of the disregard for Garbage Collection pool to a value greater than 66560.

	#jira UE-64844

Change 4431307 by Sorin.Gradinaru

	UE-64749 App crashes on device when a Widget Blueprint in use contains a Web Browser widget

	#jira UE-64749
	#Android
	#iOS
	#4.21

	Moving SharedThis(this) out of the WB Window's constructor

Change 4431161 by Robert.Manuszewski

	Fixing CIS

	#jira none

Change 4431153 by Sorin.Gradinaru

	UE-63263 Startup movies are not playing on tvOS

	#jira UE-63263
	#4.21
	#tvOS

	Startup movies need AppleMoviePlayer.uplugin, which was never enabled for tvOS.
	Same behavior as an iPhone/iPad, slightly stuttering at first run, plays fine afterwards

Change 4430958 by Matt.Collins

	Returns the default context from GetCurrentContext if none is set. Fixes crash in UE-64369. This is probably not 100% correct - we should fix all the cases where GetCurrentContext is called and a context is not set.

	#jira UE-64369

Change 4430862 by Matt.Collins

	Turning GPU Command Buffer timing back on. Enables correct profiling.

	#jira UE-64841

Change 4430813 by Marcin.Undak

	Linux: fixed editor launch with no audio device

	#jira UE-64506

Change 4430783 by Ben.Marsh

	Include Mac build files on Windows (eg. shell scripts, mono) to support remote compilation.

	#jira UE-64684

Change 4430761 by Ben.Marsh

	Fix issues with running DsymExporter remotely from a PC, and reorder engine filters to reduce the amount of junk which has to be re-transferred every time.

	#jira UE-63883

Change 4430445 by Chris.Gagnon

	Copy of CL 4416373
	Extend GCompilingBlueprint lifetime to include BroadcastBlueprintReinstanced when compiling synchronously

	#jira None

Change 4430103 by Ben.Marsh

	Make sure Newtonsoft is included in the installed engine build. (Also ignore *.build.cs that aren't enumerated from the targets we build; they won't be usable)

	#jira UE-64777

Change 4430050 by Ben.Marsh

	PR #5151: Fix BuildPlugin automation command to include Shaders folder (Contributed by pluranium)


	#jira UE-64781

Change 4430037 by Ben.Marsh

	UBT: Fix generating and checking for UHT manifest in the wrong location. If a target does not have a script plugin, the manifest should not be stored in the project binaries directory.

	#jira

Change 4430035 by Alexis.Matte

	Port a fix from cl: 4366690
	Fix the material ID when creating generating reduce LODs from a mesh where the materials was re-order to the FBX order.

	#jira UE-64028

Change 4429730 by Ryan.Vance

	#jira UEVR-1322

	Instead of tearing down and spinning up the render thread / rhi thread pipeline on pause, simply disable and enable rendering.
	This leaves the rendering infrastructure intact, but with no work to do.

Change 4429709 by Ryan.Vance

	#jira UE-64289

	Distortion correct mesh generation needs to happen on the render thread.

Change 4429690 by Ryan.Vance

	#jira UE-63538

	From 4.20.3 hotfix: 4333358
	Test render target texture size if available to ensure we don't do an extra upscale when not needed.
	The render target size is not updated when the backing render target texture is reallocated.

Change 4429671 by Ryan.Vance

	#jira UE-63333

	Lumin needs task threads to avoid saturating a single core. Reverting to previous task thread behavior for lumin.

Change 4429489 by Mitchell.Wilson

	Saving curve assets to resolve warnings.
	#jira UE-64744

Change 4429160 by Wyeth.Johnson

	First pass at integrating mass as a first class citizen. This includes solver changes, a new gravity force, and updates to a number of other force modules and templates
	#jira none

Change 4429064 by Mitchell.Wilson

	Updating default iOS version to 10 in samples and templates
	#jira UE-64815

Change 4428944 by Michael.Trepka

	Fixed issues with cursor locking on window activation on Mac

	#jira UE-13768

Change 4428879 by Ben.Marsh

	Fix error generating dsyms on Mac through the remote toolchain.

	#jira UE-63883

Change 4428729 by Ben.Marsh

	Fix warning about missing expat libs when generating projects from installed build on Mac.

	#jira UE-64740

Change 4428710 by Dan.Oconnor

	When recompiling a blueprint macro library we now do a full compile of all dependent blueprints

	#jira UE-63284

Change 4428331 by frankie.dipietro

	Update TM-FocusPriority to utilize a looping Music file

	#jira UE-29618

Change 4428295 by Rex.Hill

	Fixed debug hud not drawing during simulate in editor
	#jira UE-64704

Change 4428255 by Zachary.Wilson

	Removing Bokeh and Gaussian DoF tests that were displaying a warning
	#jira UE-64659

Change 4428236 by Andrew.Rodham

	Sequencer: Fixed edge case where equvalent range bounds were not considered equal

	#jira UE-64742

Change 4428210 by zak.parrish

	#JIRA UE-64681

	Getting proper save reporting in place on AR Save Load

Change 4428146 by Ben.Marsh

	Fix CIS error.

	#jira

Change 4428046 by Chris.Babcock

	Fix bad merge

	#jira UE-64784
	#ue4
	#android

Change 4427865 by Ben.Marsh

	Add missing include causing CIS error.

	#jira

Change 4427846 by Ben.Marsh

	When we detect a hang and send an ensure crash report, include the callstack of the thread that hung.

	#jira UE-63982

Change 4427716 by Peter.Sauerbrei

	deprecate IOS 9
	#jira UE-64018

Change 4427575 by Cody.Albert

	Fixed ShooterGame best score logic

	#jira UE-64514

Change 4427537 by Frank.Fella

	Niagara - Disable the houdini plugin in engine test since we're not actively testing it and it's causing lots of cooking problems.
	#jira UE-64736

Change 4427273 by Stefan.Boberg

	PR #5123: Small typo in Engine.h (Contributed by Vatyx)


	#jira UE-64496

Change 4427126 by Simon.Tovey

	Merging from Dev-Niagara 4427076

	Fix for static analysis warnings.

	#jira UE-64717

Change 4427014 by Simon.Therriault

	Fix for MediaFrameworkUtilities plugin

	#jira UE-64774

Change 4426983 by Ben.Marsh

	Remove natvis file from UE4 project.

	#jira

Change 4426920 by Sorin.Gradinaru

	Unshelved from pending changelist '4398174':

	UE-54804 Graphical stuttering for weapon model on Lenovo S939 in First Person Template Project

	#jira UE-54804
	#Android
	#4.21

	The bug was generated by CL3840215 - the fix for UE-53592: Assertion right after rendering scene on Lenovo S939. I've retested UE-53592 on my side and it's OK.

	Also, when building a sample (eg. First Person) on //Dev-Main, the game crashed at start with:
	D/UE4 (22619): Assertion failed: glProgramParameteri [File:C:/Work/UE4_Main/Engine/Source/Runtime/OpenGLDrv/Private\Android/AndroidOpenGL.h] [Line: 479]

Change 4426571 by Ben.Marsh

	Fix UAT compile error.

	#jira

Change 4426569 by Ben.Marsh

	UnrealVS: Fix single-file compile not working for foreign projects where the engine folder contains a space.

	#jira UE-64752

Change 4426529 by Ben.Marsh

	Various fixes/improvements to program plugins.

	* Runtime plugins are no longer loaded for every program (as was the case in 4.20)
	* If a module is whitelisted for a specific program, it will be enabled regardless of whether the Type is  "Program" or not.
	* The bCanBeUsedWithUnrealHeaderTool flag in the plugin descriptor is now translated into support for the "UnrealHeaderTool" program on load.
	* Restored support for the RuntimeAndProgram module type.
	* Plugins are now enabled in UHT by passing the project on the command line, rather than passing explicit plugins to enable.

	#jira UE-64247, UE-64595

Change 4426481 by Jeff.Fisher

	UE-64597 //UE4/Release-4.21 - Build DDC Win64 - BP_DebugMenu has been saved with empty engine version
	-resaved
	#jira UE-64597

Change 4426400 by Ben.Marsh

	Fix extra blank line at the end of ManagedProcess output.

	#jira UE-64780

Change 4426398 by Arciel.Rekman

	Linux: fix ARM server build (UE-64775).

	#jira UE-64775

Change 4426126 by Dan.Oconnor

	Harden EdGraphPin serialization

	#jira UE-64500

Change 4426071 by Dan.Oconnor

	Added logging to Blueprint merge operation
	#jira UE-58891

Change 4426061 by Phillip.Kavan

	Fix for an EDL runtime assertion on load when constructing the CDO for a nativized Blueprint class that contains an instanced object reference of another nativized Blueprint class type that references an instance of a non-nativized Blueprint class as a nested, class-owned subobject.

	- Mirrored from //UE4/Dev-Framework (4413311).

	#jira UE-62514

Change 4426031 by Phillip.Kavan

	Fix potential data loss in inherited SCS component default value overrides at runtime in a cooked build for an instance of a non-nativized child Blueprint class that inherits from a nativized parent Blueprint class.

	- Mirrored from //UE4/Dev-Framework (4365827).

	#jira UE-63757

Change 4425975 by Phillip.Kavan

	Fix potential CDO validation failures at cook time during the C++ code generation pass when Blueprint nativization is enabled.

	- Mirrored from 4371073.

	#jira UE-63647

Change 4425952 by Chris.Babcock

	Add notification channel for Android 26 compatibility

	#jira UE-62741
	#ue4
	#android

Change 4425916 by Dan.Oconnor

	Added logging to package reloading
	#jira UE-63834

Change 4425857 by Rolando.Caloca

	UE4.21 - FIx crash on html5

	#jira UE-64690

Change 4425808 by Jeff.Fisher

	Merging using Dev-VR_to_Release-4.21 Change: 4425273 UEVR-1333 MRMesh normals problems
	-Normals should not be world scaled.
	#jira UEVR-1333

	Files:
	//UE4/Dev-VR/Engine/Plugins/Lumin/MagicLeap/Source/MagicLeap/Private/MeshTrackerComponent.cpp#13

Change 4425722 by Rolando.Caloca

	UE4.21 - Android compile fix

	#jira

Change 4425408 by Phillip.Kavan

	Fix UBT warnings when building/packaging with Blueprint nativization enabled.

	- Mirrored from //UE4/Dev-Framework (4352144).

	#jira UE-62946, UE-63311

Change 4425238 by frankie.dipietro

	Update TM-FocusPriority so that tests may utilize 8 audio channels

	#jira UE-29618

Change 4425232 by Jamie.Dale

	Fixed crash when re-pointing an object referenced in Python

	#jira UE-64363

Change 4425194 by Phillip.Kavan

	Support a proper fallback to construct any native 'noexport' struct-typed literal term in a nativized Blueprint class.

	- Mirrored from //UE4/Dev-Framework (4347635).

	#jira UE-63364

Change 4424786 by Brian.Zaugg

	Add support for new iOS UserNotifications framework, enabled if min iOS version is set >= 10.

	#jira UE-51041, UE-59178

Change 4424584 by Ben.Marsh

	Include the Visual Studio .natvis file as a solution item, to fix problems with VS2017 15.8.5 not finding it in a makefile project.

	#jira UE-64733

Change 4424410 by Max.Chen

	Sequencer: Fix deprecation

	#jira UE-48190

Change 4424364 by Frank.Fella

	Niagara - Fix static analysis for SItemSelector.h - Preflight passed.
	#jira UE-64719

Change 4424291 by Shaun.Kime

	Volume texture work should not be pulled over from Dev-Niagara yet
	#jira UE-64756

Change 4424021 by Jason.Bestimt

	Merging CL 4401151 from Dev-VR - Adding destructor to SoundPresets

	#JIRA:none

Change 4423955 by Brandon.Schaefer

	Linux: Add a timer when we lose focus to check if we have lost application focus

	#jira UE-63931

Change 4422974 by Robert.Manuszewski

	Fixing CIS

	#jira UE-64741

Change 4422709 by Jason.Bestimt

	Merging CL# 4419752 from Dev-VR - Fixes to prevent audio from crashing on exit

	#JIRA: none

Change 4422691 by Jamie.Dale

	Fixing localization warnings

	Added support for gathering from the newly added UI_COMMAND_EXT macro

	#jira

Change 4422394 by Sorin.Gradinaru

	UE-64066 TM-Gameplay Load Game does not work on TVOS

	#jira UE-64066 TM-Gameplay Load Game does not work on TVOS
	#tvOS
	#4.21

	Blocker: GRHIHDRDisplayOutputFormat must have a default value for tvOS (PF_B8G8R8A8, just like mac or ios for non-hdr). The issue appeared at CL4092793 [at] //UE4/Dev-Rendering, 5/24/2018

Change 4422285 by Jamie.Dale

	Fixing localization warnings

	#jira

Change 4421911 by Chris.Babcock

	Fix issues with LLDB data formatters with 2 byte characters

	#jira UE-64737
	#ue4

Change 4421833 by Wyeth.Johnson

	#jira 64735
	Sample Skel Mesh refactor to fix it not initializing, still bugged for vertex colors

Change 4421316 by Jamie.Dale

	Fixing localization warnings

	#jira

Change 4421223 by Ben.Marsh

	Add an experimental setting to enable support for long paths in the content browser.

	#jira

Change 4421197 by Jamie.Dale

	Fixing localization warnings

	#jira

Change 4421191 by Jamie.Dale

	Fixing localization warnings

	#jira

Change 4421179 by Rolando.Caloca

	UE4.21 - Vulkan - Add missing viewport resize function
	- Separate num buffers per usage

	#jira

Change 4420955 by Michael.Trepka

	Ignore tooltips and cursor decorator windows when determining the window under cursor on Mac

	#jira UE-64645

Change 4420323 by Robert.Manuszewski

	Reimplementing CL #4402016 from Dev-Core:

	Fixed cooked materials not working in the editor

	#jira UE-64389

Change 4420227 by Rex.Hill

	Fix USD Linux compiler warning

	#jira UE-63658

Change 4420198 by Ben.Marsh

	UBT: Detect changes to source directories and invalidate the makefile if they are newer. Forces the makefile to be rebuilt when files are added or removed.

	#jira UE-42601

Change 4420194 by Mitchell.Wilson

	Saving color curve assets to resolve warnings.
	#jira UE-64724

Change 4419740 by Jamie.Dale

	Don't force LocRes files to always use Unicode strings

	The automatic Unicode detection works just fine for them, and changing this can produce considerably smaller (up-to 50% smaller!) LocRes files

	#jira

Change 4419728 by Mikey.Boyd

	Updating TM-MaterialAttributes map and MaterialAttributes_Widget for cleaner testing of UEQATC-3497. Set the level to the default GameMode, updated the level blueprint, and disabled interaction with the widget bp.

	#jira UE-29618

Change 4419646 by Ben.Marsh

	UBT: Determine the correct user directory when remote compiling on Mac, rather than inferring it from the user name. The user name can be changed after an account is created.

	#jira UE-64071

Change 4419529 by Jules.Blok

	Merging CL 4323685 from Dev-VR - Fix GLES occlusion queries when HDR is disabled.
	Add occlusion query support for Multi-View.
	Add ShowFlag to visualize occlusion query bounding meshes.

	#jira UE-62347

Change 4419528 by Jules.Blok

	Merging CL 4323400 from Dev-VR - Remove StereoPair in favor of visibility map merging introduced in CL 4036044

	#jira UE-62738

Change 4419525 by Stefan.Boberg

	Reduce CPU usage for Cook-on-the-fly server by not busy-waiting for assets to cook.

	Added an event which is signaled when a network request has been received. The main COTF loop blocks on this event when the cook request queue is empty. This reduces CPU usage while keeping the request latency low.

	#jira UE-51348
graeme.thornton, martin.ridgers

Change 4419034 by Ben.Marsh

	Allow plugins specified on the command line to override engine plugins.

	#jira UE-64685

Change 4419015 by Frank.Fella

	Niagara - Fix linux compile error from CL 4418815.
	#jira UE-63379

Change 4418931 by Frank.Fella

	Niagara - Fix Win32 compile issue caused by cl 4418639
	#jira UE-61823

Change 4418887 by Frank.Fella

	Merging from Dev-Niagara all plugin content updates.
	#jira UE-64711

Change 4418883 by Uriel.Doyon

	Fixed GPU emitters not being ticked correctly on the renderthread.

	#jira UE-64710

Change 4418817 by Chris.Babcock

	Fix for x86 and x86_64 Android architectures with NDK14b

	#jira UE-64708
	#ue4
	#android

Change 4418815 by Frank.Fella

	Merging from Dev-Niagara 4399353 for nicholas.goldstein

	Improve discoverability of and navigability to parent Emitters in Systems
	#jira UE-63379

Change 4418709 by Frank.Fella

	Merging from Dev-Niagara 4397394 for nicholas.goldstein

	Add missing entries and param creation subcategory to Set Variables module.
	#jira UE-63373

Change 4418639 by Frank.Fella

	Merging from Dev-Niagara 4360525, 4360676, 4364096 for Simon.Tovey

	Some compile time optimizations

	- Moved large map of hlsl keyword replacements from C++ to ini file to improve compile times.
	- Now not using templates to handle input parameters to data interface. Now taking a bit from the bytecode offset to denote constant vs register.
	- As well as improving compile times, this also makes DIs far easier to write and maintain so I've rolled out to all DIs. Has no affect on perf.
	- Moved some things into the VectorVM namesspace.

	#jira UE-61823

Change 4418333 by Mark.Satterthwaite

	Fix incorrect usage of a CFDictionary passed into IOServiceGetMatchingServices which releases it for us. #rb none #jira UE-62569

Change 4418323 by zak.parrish

	Checking in files for SaveLoad - almost done. Need to update save status and do the helpAbout screen
	#JIRA UE-64681

Change 4418307 by Frank.Fella

	Merging from Dev-Niagara 4357655 for nicholas.goldstein

	Fix various capitalization inconsistencies in niagara stack menus
	#jira UE-63371
	#jira UE-63446

Change 4418242 by Frank.Fella

	Merging from Dev-Niagara 4356331 for nicholas.goldstein

	Add Spacer between Event Handler header row and added Event Handler properties
	#jira UE-63390

Change 4418222 by Dan.Oconnor

	Close blueprint editor when replacing a blueprint, avoiding crash when using the merge tool to reload a blueprint

	#jira UE-64435

Change 4418217 by Frank.Fella

	Merging from Dev-Niagara 4356318 for nicholas.goldstein

	Adjust DisplayName generation of linked inputs in Niagara Stack input options to show the entire namespace and to not pass through FName::NameToDisplayString
	#jira UE-63387
	#jira UE-60349

Change 4418163 by Frank.Fella

	Merging from Dev-Niagara 4346597, 4346602, 4346709, 4353333, 4353344, 4353346, 4353355, 4355466, 4355948, 4356192

	Niagara - New emitter and new system dialogs

	#jira UE-63765

Change 4418114 by Mark.Satterthwaite

	Assert that Metal heaps and sub-allocated buffers are actually created properly to catch OoM errors. #rb none #jira UE-62235

Change 4417947 by Ben.Marsh

	Compile DebugGame version of the launch module for installed builds.

	#jira UE-64604

Change 4417554 by Joe.Conley

	Merging using Dev-VR_to_Release-4.21

	#jira UE-63210 - "Stereo layers persist in HMD after VR Preview"
	#jira UE-54565 - "Packaged game/Editor with Stereo Layers crashes when exiting game through steam VR overlay"

	Remove check(VROverlay) in FSteamVRHMD::GetStereoLayers() and just return nullptr if it is null.

	Revert previous fix for UE-54565 that caused UE-63210

Change 4417553 by Joe.Conley

	Merging using Dev-VR_to_Release-4.21

	#jira UE-64054 MRC - Mixed Reality Calibration app does not let users toggle between attachment devices.

	Enumerate Motion Sources was getting called before the Motion Controller modular features were being added.

	Added a delay before calling Enumerate Motion Sources.

Change 4417552 by Joe.Conley

	Merging  to Release-4.21 (//UE4/Release-4.21)

	#jira UE-63579 Assert when having too many VR devices

	Increase max controllers to support more.

Change 4417533 by Ben.Marsh

	Fix IPhonePackager not being included in installed builds created on a Mac.

	#jira UE-58493

Change 4417495 by Joe.Conley

	Back out changelist 4417484

	Integrate automatically submitted this before I could convert it to an edit, back out.

	#jira UE-63579 Assert when having too many VR devices

Change 4417484 by Joe.Conley

	Merging using Dev-VR_to_Release-4.21

	#jira UE-63579 Assert when having too many VR devices

	Increase max controllers to support more.

Change 4417438 by Ben.Marsh

	Prevent unzipped frameworks from being removed every time a remote IOS compile is started, preventing iterative builds.

	#jira

Change 4417422 by Ben.Marsh

	PR #4937: UE-62508: Add option to project launcher to include prerequisites com. (Contributed by projectgheist)

	#jira UE-62508

Change 4417362 by Ben.Marsh

	Fix compile warning when converting a blueprint project to native.

	#jira UE-63802

Change 4417357 by Jerome.Delattre

	Copying from Dev-Enterprise CL 4364256
	Fix typo in FGenericPlatformMemory::DumpStats to properly report the free virtual memory.

	#author Martin.Sevigny

	#jira

Change 4417350 by Jerome.Delattre

	Copying from Dev-Enterprise CL 4364260
	SceneCapture: Don't CaptureOnMovement if CaptureEveryFrame is enabled. This prevent the capture from happening twice in 1 frame.

	#author Martin.Sevigny
	#jira UE-64686

Change 4417333 by Uriel.Doyon

	Reduced the amount of interpolants in FVertexFactoryInterpolantsVSToPS by the Niagara when using DynamicParameters.
	Now only the param required are used where as before the count was defined by the max index.
	#jira UE-63712

Change 4417318 by Dan.Oconnor

	Improve error messaging when a class creates its CDO while the class layout is being created

	#jira UE-64534

Change 4417276 by Frank.Fella

	Merging 4340773 from Dev-Niagara for nicholas.goldstein
	Add UProperty Specifiers to PropertyMetaData to fix displayed name and tooltip
	#jira UE-63233

Change 4417226 by Frank.Fella

	Merging 4340719 from Dev-Niagara for nicholas.goldstein
	Disable Widget focus of expander arrows and pulldown arrows in Niagara Stack to prevent unexpected tab selection
	#jira UE-63380

Change 4417196 by Patrick.Boutot

	Create a 10bit media texture for CharBGR10A2 media format.

	#jira UE-64682

Change 4417193 by Patrick.Boutot

	Keep a reference to the current MediaProfile's proxy to prevent GC
	Move MediaProfileSettings to default config.

	#jira UE-64475

Change 4417192 by Patrick.Boutot

	Deprecate ABGR16 and Float RGB from EDefaultBackBufferPixelFormat. They are not supported by D3DX SwapChain. The jira has more info on which formats are supported.
	#jira UE-63090

Change 4416191 by zak.parrish

	Resaving Debug menu for TP_ARBP template
	#JIRA UE-64678

Change 4416030 by Marc.Audy

	Remove broken test (as best it can be told it could never have worked)
	#jira UE-64547

Change 4415784 by Marc.Audy

	PR #5133: UE-64479 Fix the icon display for the Timeline node replication status (Contributed by mamoniem)
	PR #5132: UE-64479: Replicated icon shows up for timelines in BP (Contributed by projectgheist)
	#jira UE-64479
	#jira UE-64596
	#jira UE-64606

Change 4415763 by Steve.Robb

	Fixes for improperly initialized USTRUCT() members.

	#jira UE-64551
	#jira UE-64546

Change 4415723 by Rolando.Caloca

	UE4.21 - Remove Linux hack for Vulkan

	#jira

Change 4415679 by Rolando.Caloca

	UE4.21 - Copy fix from 4415584 and 4401641
	- Added support for -vulkanpresentmode=N where N is a VkPresentModeKHR
	- This prevents the CPU from getting more than one frame ahead of the GPU. In addition to making the Vulkan RHI more consistent with the DX RHIs and reducing game input latency, this also fixes the issue where memory for volatile uniform buffers could be overwritten by the CPU before the GPU was done with it. This could happen when running in immediate present mode with frame pacing and smoothing disabled, as the CPU could get several frames ahead of the GPU.

	#jira

Change 4415654 by Jamie.Dale

	Fixed the editor failing to load the native game localization data during init

	#jira UE-64097

Change 4415635 by Jamie.Dale

	Fixed null pointer access in setting helpers

	#jira

Change 4415630 by Jamie.Dale

	Ensure we GC at various points that could have left lingering references to items being content hot-reloaded

	#jira

Change 4415624 by Jamie.Dale

	Ensure that the transient instance created by the material editor is marked pending-kill once the material editor is closed

	This fixes an issue where a lingering material editor instance would re-apply old material constant values after a content hot-reload, as the content hot-reload would call PostEditChange on the lingering instance.

	#jira

Change 4415620 by Jamie.Dale

	Fixed type validation of objects within containers

	#jira UE-64151

Change 4415613 by Jamie.Dale

	Fixed crash when deleting a package without an SSC state

	#jira

Change 4415592 by Marcin.Undak

	UAT: Aways create directory before saving encryption parameters

	#jira none

Change 4415590 by Jamie.Dale

	Merging CL# 4357533, CL# 4366557 and CL# 4374101

	#jira

Change 4415577 by Marcin.Undak

	Linux: fixed WebM crash

	#jira UE-64157

Change 4415340 by frankie.dipietro

	Empty M_VolumeSampler for testing

	#jira UE-29618

Change 4415334 by frankie.dipietro

	Adjust M_VolumeSample test material to use Emissive

	#jira UE-29618

Change 4415085 by Ben.Marsh

	Restore const signature for FApplePlatformCrashContext::ReportCrash() for compatibility with callbacks.

	#jira

Change 4415032 by Ben.Marsh

	Fix Mac compile error due to calling non-const function.

	#jira

Change 4415005 by frankie.dipietro

	Add M_VolumeSample to QAGame to aid Volume Texture testing

	#jira UE-29618

Change 4414930 by Ben.Marsh

	Include portable callstacks in ensures.

	#jira UE-64574

Change 4414631 by Graeme.Thornton

	Fix for crash when running automation tests

	#jira UE-64652

Change 4414055 by Dan.Oconnor

	Remove use of FAssetData to avoid side effects that can result in CDO creation

	#jira UE-64534

Change 4413933 by Jeff.Fisher

	Merging using Dev-VR_to_Release-4.21

	4374589 UE-57680 [CrashReport] UE4Editor_SteamVR!FSteamVRModel::GetRawMeshData() [steamvrassetmanager.cpp:307]
	-Added some more logging to try to figure out why this is crashing.  Logs the resourceID of the steamVRMesh which might tell us if this is happening with some specific hardware.  Also logs index and vertex counts and pointers to see if the data is somewhat malformed or just completely invalid.

	4380355 UE-57680 [CrashReport] UE4Editor_SteamVR!FSteamVRModel::GetRawMeshData() [steamvrassetmanager.cpp:307]
	-Two more log lines for steamvr shutdown.  Maybe we are trying to get the models after shutdown and get bad data?

	#jira UE-57680


Change 4413599 by Mitchell.Wilson

	Saving SunlightColorLUT to resolve warning.
	#jira UE-64648

Change 4413343 by Ben.Marsh

	Add Rsync filters to installed build on Windows.

	#jira UE-64623

Change 4413276 by Ben.Marsh

	Remove logic that prevents UHT compiling on Mac during a remote build.

	#jira UE-64493

Change 4413205 by Ben.Marsh

	Fix missing dependencies from installed build on Mac and Linux.

	#jira UE-64629

Change 4413154 by Jason.Bestimt

	Merging CL 4379292 from Dev-VR - Fixes for mobile static draw list using incorrect material render proxy

	#JIRA: UEVR-1321

Change 4413148 by Jason.Bestimt

	Merging CL 4373138 from Dev-VR - Fix for media texture crash on Lumin

	#JIRA: UEVR-1319

Change 4413142 by Jason.Bestimt

	Merging CL 4347297 from Dev-VR - Swapping double to single quote to fix Mac Editor packaging for Lumin

	#JIRA: UE-63556

Change 4412619 by Jason.Bestimt

	Merging CL 4331753 from Dev-VR - Allowing output log to capture warning and error verbosity from commandline output it receives (like launch on Lumin)

	#JIRA: UE-62981

Change 4412526 by Jason.Bestimt

	Merging CL 4311018 from Dev-VR - Preventing multiple lumin frame timing hints while the graphics context isn't valid

	#JIRA: UE-62031

Change 4412517 by Jason.Bestimt

	Merging CL 4304939 from Dev-VR - Commenting out Eye Tracker init log until the feature is complete.

	#JIRA: UE-61413

Change 4412515 by Jason.Bestimt

	Merging CL 4304711 from Dev-VR - Downgrading sandbox jail warning to log, as it is expected output

	#JIRA: UE-62992

Change 4412508 by Jason.Bestimt

	Merging CL 4303846 from Dev-VR - Fix for fully qualified name in header

	#JIRA: UE-62068

Change 4412496 by Jason.Bestimt

	Merging CL 4303647 from Dev-VR - Fixing synth style warnings in Lumin Sample

	#JIRA: UE-62068

Change 4412491 by Jason.Bestimt

	Integrating CL 4303618 from Dev-VR to remove Lumin Emulator plugins

Change 4412425 by Mitchell.Wilson

	Updating GetLinearColor nodes in BP_Sky_Sphere to GetClampedLinearColor nodes. Saving curves due to the change to resolve warnings.
	#jira UE-64228

Change 4412349 by Lauren.Ridge

	Adding custom version to linear color curve assets
	#jira UE-64228

Change 4412302 by Ben.Marsh

	Include the full filename of the config file if we can't save a backup. Attempt to track down UE-64554.

	#jira UE-64554

Change 4412161 by frankie.dipietro

	Adding bUsedWithStaticLighting flag to M_Roughness_0 to fix RectLight test

	#jira UE-29618

Change 4411692 by tim.gautier

	Minor adjustment to M_Tree_FallTreeMerged_01 - added a StaticSwitchParam
	#jira UE-29618

Change 4411522 by Lauren.Ridge

	PostLoad check and additional GetClampedColorValue BP function for linear color curves
	#jira  UE-64228

Change 4411494 by Francis.Hurteau

	Copied 4359025 Francis.Hurteau
	Test if have a valid continuation before running it when setting continuation on a ready future

	#jira UE-64567

Change 4411151 by Jeff.Fisher

	Merging using Dev-VR_to_Release-4.21

	4345353 UE-62255 Google 6DOF Motion Controller Support
	-Added support for 6DOF controller tracking on daydream.
	-Project Settings->Android->Advanced APK Packaging->Configure GoogleVR to support specific hardware configurations-> Set to Daydream(6.6 DoF) to enable support for two 6dof controllers. A single 3dof controller is also supported by this configuration, as is a single 6dof controller.
	-Currently the 3dof controller is detected only by noticing that the positions returned for all controllers are exactly 0,0,0 per Google reccomendation.  A future API should make it explicit when a controller is 3dof or 6dof.
	-Currently we arbitrarily make the controller at index 0 the right and the one at 1 the left.  A future api should let us query for controller handedness.

	4346387 Fixing build break in GoogleVRControllerFunctionLibrary.h

	4349142 Fixing GoogleVRController build warning

	4349742 Fixing AndroidRuntimeSettings build errors.

	4350977 Fix for static analysis warning

	4351170 Build fix for bad assert

	#jira UE-62255

Change 4410947 by Lina.Halper

	#DUPEMERGE

	Fixes many reference pose issues, where they want to add node, and how it updates, and how they could move

	- mainly execution can be disabled by button, and  we will mark to compile when it needs recompile due to hierarchy change
	- added quick warning to log when hierarchy is missing
	- still lots of things need more iteration
	- fixed assets

	#jira: UE-63136

Change 4410550 by Lauren.Ridge

	Fix for ListView not propagating selection change events
	#jira UE-64118

Change 4410527 by Lauren.Ridge

	Adding condition to guard against crash when pasting comments
	#jira UE-63214

Change 4410512 by Marcin.Undak

	UAT: Create manifest directory, before writing to it

	#jira none

Change 4410511 by Lauren.Ridge

	Fix for crash on spamming trigger presses in VR mode
	#jira UE-63608

Change 4410489 by Lauren.Ridge

	Fix for disappearing UI handle in VR mode
	#jira UE-63622

Change 4410364 by Ben.Marsh

	PR #5138: Fix bForceBuildShaderFormats not being read correctly in TargetRules (Contributed by rlabrecque)

	#jira UE-64611

Change 4410086 by Ben.Marsh

	UAT: Show timing/agent info in UAT the same way as in UBT, to make it easier to track down badly configured agents.

	(Originallly CL 4343393 in Dev-Build)

	#jira

Change 4410018 by Lina.Halper

	#DUPE MERGe
	Merging using Dev-Anim<->Release4.21

	fix motion blur issue on poseable mesh component

	#jira: UE-62296

Change 4409981 by Simon.Tourangeau

	SunPosition plugin

	#jira UE-64075

Change 4409913 by Michael.Kirzinger

	Fix FUniqueNetIdRepl serialization for out of engine OSSs. Use FNetBitReader/Writer in the serialization test so it properly tests network replication

	#jira UE-63326
[at]josh.markiewicz

Change 4409838 by Simon.Tourangeau

	Integrate misc nDisplay fixes from Dev-Enterprise

	- nDisplayLauncher: Removed useless no-val command line arguments
	- Fixed Editor crash when translation direction is set in BP_DemoWand_Blueprint
	- Use quotation marks for paths
	- A lot of nDisplay warings "wand not found"
	- nDisplayLauncher passes custom arguments at beginning
	- Update blueprints (resubmit + comments)
	- Single view for monoscipic devices
	- Move swap eye attribute to [cluster_node]
	- Removed viewport flip feature
	- Disable input on slave nodes
	- Fix for GL3/4 monoscopic device. Missed changes for 'Viewport positioning fix'.
	- Removed custom swap_int
	- Fixed demo blueprints
	- Fixed crash in DC components when the DC is not initialized
	- Fixed visibility of some inherited members
	- Fixed Editor crash on second PIE session start
	- Fix for viewport positioning
	- BP API docs (comments)
	- Turn off some screen effects in the nDisplay template project

	#jira UE-64170

Change 4409812 by Simon.Tourangeau

	USD SDK updated to v18.09

	Merged from Dev-Enterprise

	#author Rex.Hill
	#jira UE-64140

Change 4409754 by Graeme.Thornton

	Copy of CL 4385549 from Dev-Core

	Try and detect a serialize function inside arbitrary preprocessor blocks and throw an error
	Make a couple of other "can't have things inside preprocessor blocks" messages consistent with this one

	#jira none

Change 4409671 by Jeff.Fisher

	Merging using Dev-VR_to_Release-4.21

	4392743 UE-63646 MagicLeap 4.21 redirects need updating
	-Made redirectors more specific, found a way to do the updates without the MatchSubstring package update flag, to avoid a warning about how it is very slow.

	#jira UE-63647

Change 4409395 by Ben.Marsh

	Fix Python.build.cs adding locally generated *.pyc files as runtime dependencies. Since these files are not tracked as build products, they will not exist on other build machines, causing warnings when building DDC.

	#jira

Change 4408881 by Max.Chen

	Sequencer: Added missing ini change for 4395032

	#jira UE-64258

Change 4408670 by Max.Chen

	Sequencer: Step to next/prev key no longer uses ExclusiveEndFrame - 1. This fixes cases where you want to step to the last key of a section and add another animation clip that doesn't overlap.

	#jira none

Change 4408662 by Max.Chen

	Sequencer: Removed derived blueprint type for level sequence director blueprints

	#jira UE-64258

Change 4408661 by Max.Chen

	Sequencer: When evaluating a range of a sequence, all overlapping segments of that range are now guaranteed to be up-to-date and compiled.

	This is a newer version of 4280319 which was backed out in 4317353, with the TRange::Overlaps issues addressed.

	#jira UE-63336
	#jira UE-62873

Change 4408660 by Max.Chen

	Sequencer: Fix crash when converting matinee assets to sequences due to changes in events. Event keys will now have blueprint event endpoints created in the level sequence blueprint when converting.

	#jira none

Change 4408354 by Marc.Audy

	Fix additional cases where GetBlueprint could get called without a valid blueprint, thus checking
	#jira UE-62444

Change 4408286 by Joe.Conley

	#jira UE-64552 [CrashReport] UE4Editor_MagicLeap!UImageTrackerComponent::TickComponent() [imagetrackercomponent.cpp:391]

	MagicLeap ImageTrackerComponent: Adding check for PLATFORM_LUMIN to prevent PIE crash running code that was designed to only run on device.

	This had been fixed previously but somehow the fix got lost merging between streams.

Change 4408252 by Brandon.Schaefer

	Doubles in the form of 1e+9999 can be larger then 30 chars, set aside more room

	#jira UE-64582

Change 4408076 by Mike.Erwin

	UAT: fix Iterative Deploy leaving behind old files on Android
	(copy 4369737 from Dev-Build)

	For iterative deploy on Android, files & directories not in staging will be zapped from the device.

	The following directories are spared, to maintain config & logs on device:
	Engine/Saved
	Engine/Config
	ProjectName/Saved
	ProjectName/Config

	Tested with Samsung Galaxy S6+ (Android 6.0) and Google Pixel 2 (Android 9.0)

	#android
	#jira UE-61686

Change 4408070 by Mike.Erwin

	UAT: iterative deploy no longer prevents cleaning of staging directory
	(copy 4369732 from Dev-Build)

	Partial fix for
	#jira UE-58838 UE-61686 UE-62879

	Might need additional work to reduce file copies from Build to Stage.

Change 4407741 by Ben.Marsh

	UBT: Avoid trying to figure out which mobile provision to use for a project when we already know the path to the provision we want. Also fix certificate not being updated if mobile provision changes.

	#jira

Change 4407632 by Jamie.Dale

	Removed pyc files from Python SDK

	#jira UE-64531

Change 4407622 by Jamie.Dale

	Updated Python SDK copy script to exclude pyc files

	#jira UE-64531

Change 4407431 by Anousack.Kitisa

	Copied 4392037 Anousack.Kitisa
	Modified API for UV map generation.

	#jira UE-63814

Change 4407427 by Ben.Marsh

	Suppress warning for missing DebugNonUFS files when staging installed build.

	#jira UE-64579

Change 4407401 by Anousack.Kitisa

	Copied 4383601 Anousack.Kitisa
	[StaticMeshEditor]:
	* Fixed material assignment issue when re-importing Datasmith static mesh after deleting a section from the mesh.
	* Fixed material issue when deleting a section from a mesh and undoing and chaging material on the mesh.

	#jira UE-62480

Change 4407274 by Joe.Graf

	Over merge reverted

	#jira: fix

Change 4407268 by Anousack.Kitisa

	Copied 4369800 Anousack.Kitisa
	Import/export FBX metadata on SkeletalMesh and Skeleton assets.

	#jira UEENT-1881

Change 4407220 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - AR samples updated

	#jira: UE-64673

Change 4407213 by Rolando.Caloca

	UE4.21 - Copy 4403346
	DR - Fix post opaque extension running before lights

	#jira

Change 4407208 by Anousack.Kitisa

	Copied 4369604 JeanLuc.Corenthin
	- Fixed indexing issue with FOverlappingCorners object created using a FmeshDescription object:
	    - Changed logic in building of FOverlappingCorners to be based on index of wedge, number of wedges equal 3 times the number of triangles like in FRawMesh, instead of index of vertex instances.
	   - Updated BuildVertexBuffer to take this new logic in account.
	- Fixed crash in FMeshDescriptionOperations::CreateMikktTangents:
	   - MikkTSpace sdk does not handle properly faces with 0 vertices which occur with mesh description with invalid polygons.

	#jira UE-62478, UE-63758

Change 4407188 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Fixed missing include when not using unity builds

	#jira: UE-64673

Change 4407179 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Added a way to see the ARKit CPU consumption across all of its threads

	#jira: UE-64673

Change 4407178 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Bug fix for landscape image detection being at the wrong distance

	#jira: UE-64673

Change 4407174 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Improved debug rendering of AR tracked images

	#jira: UE-64673

Change 4407165 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Added post edit change to force correct aspect ratios when editing physical size for detected images

	#jira: UE-64673

Change 4407162 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Reverted premature merge

	#jira: merge

Change 4407156 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21

	#jira: merge

Change 4407146 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Point cloud rendering additions

	#jira: UE-64673

Change 4407134 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Added some config properties to box component for debug rendering

	#jira: UE-64673

Change 4407128 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Added missing #ifdef

	#jira: UE-64673

Change 4407127 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - AR point cloud debug rendering support added

	#jira: UE-64673

Change 4407119 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - ARVideoSettings sample

	#jira: UE-64673

Change 4407113 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Added a device orientation query in addition to the event that is present

	#jira: UE-64673

Change 4407109 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Fixed a missing nullptr check that was causing AR apps to crash

	#jira: UE-64673

Change 4407105 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Added support for rotating an image while generating a jpeg for ARSharedWorld sample

	#jira: UE-64673

Change 4407101 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Added a set of properties to override thread priorities when using ARKit and UE4 is causing ARKit to starve

	#jira: UE-64673

Change 4407098 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21

	#jira: merge

Change 4407093 by Francis.Hurteau

	Copied 4372687 Francis.Hurteau
	Fix potential assert when changing ProtocolVersion based on ping messages

	#jira UE-64035

Change 4407088 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Added scaling to jpeg creation for ARSharedWorld sample

	#jira: UE-64673

Change 4407082 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - ARSharedWorld sample

	#jira: UE-64673

Change 4407078 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Fix for null session config being passed to AR system

	#jira: UE-64673

Change 4407074 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - ARSharedWorld sample

	#jira: UE-64673

Change 4407064 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - ARSharedWorld sample

	#jira: UE-64673

Change 4407061 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - ARSharedWorld sample

	#jira: UE-64673

Change 4407052 by JeanLuc.Corenthin

	Copy CL #4392021
	  - Fixed crash when importing model thru VRED importer
	  - Includes changes to StaticMeshBuilder.cpp made in CL #4369604

	#jira UE-64152

Change 4407051 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - ARSharedWorld sample

	#jira: UE-64673

Change 4407045 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Code suupport for streaming ar shared worlds

	#jira: UE-64673

Change 4407039 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - ARSharedWorld sample

	#jira: UE-64673

Change 4407032 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Improved AR template

	#jira: UE-64673

Change 4407030 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Better BP exposure of GetMappingStatus for AR

	#jira: UE-64673

Change 4407017 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Better double free fix for ar env probes

	#jira: UE-64673

Change 4407012 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Double free fix for env probes

	#jira: UE-64673

Change 4407007 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - AREnvProbe sample

	#jira: UE-64673

Change 4407006 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - AREnvProbe sample

	#jira: UE-64673

Change 4407003 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - AREnvProbe sample

	#jira: UE-64673

Change 4406999 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Render doc fix for non-editor builds

	#jira: merge

Change 4406994 by Anousack.Kitisa

	Copied 4362955 JeanMichel.Dignard
	Static Mesh Library : SetLodFromStaticMesh
	- Assign materials to new LOD sections based on source mesh LOD.
	- Copy LOD build settings from source.

	#jira UE-63659, UE-64016

Change 4406992 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Fix for CLion IDE usage

	#jira: merge

Change 4406987 by Joe.Graf

	Merging using Dev-VR_to_Release-4.21 - Fix for missing property decoration for video modes

	#jira: UE-64673

Change 4406944 by Max.Chen

	Sequencer: Stop all sounds on tear down

	#jira UE-63501

Change 4406942 by Mark.Satterthwaite

	Duplicate CL #4398322: Rebuild mtlpp using Xcode 10 so Metal 2.1 works on iOS.

	#jira N/A

Change 4406880 by Anousack.Kitisa

	Copied 4356072 Anousack.Kitisa
	[StaticMeshEditor]:
	* Moved the UV channel removal functionality from the LOD details panel to the UV drop-down menu.

	#jira UEENT-1827

Change 4406826 by Francis.Hurteau

	Copied 4366928 Jeremie.Roy
	Fix crashes related to the UndoHistory.
	Add missing UndoBufferChangedDelegate broadcasts in EditorTransaction

	#jira UE-64096

Change 4406820 by Anousack.Kitisa

	Copied 4350474 Johan.Duparc
	Fix UEditorAssetLibrary::DoesAssetExist to handle sub object name

	#jira UE-62333

Change 4406783 by Anousack.Kitisa

	Copied 4348861 Anousack.Kitisa
	[StaticMeshEditor]:
	* Fixed vertices being merged even though they don't share mergeable attributes.

	#jira UE-62980

Change 4406752 by Anousack.Kitisa

	Copied 4348838 Anousack.Kitisa
	Fixed ConvertAnyPathToObjectPath returning wrong path for objects that are named differently than their package.

	#jira UE-62333

Change 4406721 by Brandon.Schaefer

	Linux: Set FORCE_ANSI_ALLOCATOR when using -EnableTSan to avoid multiple definitions

	#jira UE-62804

Change 4406461 by Francis.Hurteau

	Copied 4382276 Francis.Hurteau
	Fix syncing streaming level visibility state with undo/redo

	#jira UE-64315

Change 4406459 by Ben.Marsh

	Fix temporary files containing dependency lists being created in Engine directory. Move them under Engine/Intermediate instead.

	#jira

Change 4406451 by Brandon.Schaefer

	Linux: Dont try to make current if our current window was the SplashScreen window we just destroyed

	#jira UE-64211

Change 4406440 by Chris.Babcock

	Remove AKEYCODE_BACK from valid gamepad keycodes to allow it when controllers are disabled

	#jira UE-64350
	#ue4
	#android

Change 4406382 by Anousack.Kitisa

	Copied 4393087 Patrick.Boutot

	Add a shader source directory for the OpenCVLensDistortion plugin.

	#jira UE-64568

Change 4406345 by Joe.Graf

	Merge Xcode project generation fix to defaul to Legacy build mode using Dev-VR->Release-4.21

	#jira: merge

Change 4405792 by JeanMichel.Dignard

	Copied 4393315 JeanMichel.Dignard
	Better telemetry for plugin warden
	- Added more event attributes to track potential issues.

	#jira UE-64280

Change 4405776 by JeanMichel.Dignard

	Copied 4392578 JeanMichel.Dignard
	Fixed actors not saving properly on a reimport when moved to a sublevel.
	The issue is that they were being parented to actors from another level which isn't supported.
	Also, the scene component template should refer to the parent component through a TSoftObjectPtr in case it's in another package.

	#jira UE-62070

Change 4405738 by Mark.Satterthwaite

	Duplicate CL #4396224: Remove another broken bit of legacy Metal code.

	#jira UE-64128

Change 4405733 by Mark.Satterthwaite

	Duplicate CL #4399472: Handle all Metal formats in ShaderPlatformToPlatformName - I have not made this handle all formats or platforms so this function is *wrong* for a number of others and should *assert* on failure to identify a platform. This fixes incorrect shader compilation for Forward rendering on iOS.

	#jira UE-64355

Change 4405630 by JeanMichel.Dignard

	Copied 43737703 Johan.Duparc
	Prevent crash for old datasmith assets
	Clear transactional flag in user asset data that could cause a crash for Blueprint assets.

	#jira UE-63349

Change 4405481 by Sorin.Gradinaru

	UE-61699 Kindle Fire 5th Gen crashes rotating Remote Session

	#jira UE-61699
	#Android
	#4.21

	From //UE4/Dev-Mobile CL4360704

	Disabling threaded rendering for Kindle Fire HD 7 5th gen in DefaultDeviceProfiles.ini

Change 4405276 by Frankie.DiPietro

	Adding test content for Volume Textures, Iris Normals, and Subsurface Boundary Bleed Color coverage

	#jira UE-29618

Change 4405138 by JeanMichel.Dignard

	Copied 4350470 Johan.Duparc
	Fix crash on property edition for blueprint actors with templated asset user data

	#jira UE-63349

Change 4404967 by Mikey.Boyd

	Making content change to M_Translucent_PanNrm for UEQATC-244. Added a Texture Sample node to be used to test Platform Stats.

	#jira UE-29618

Change 4404909 by Robert.Manuszewski

	When enabling cooked assets in the editor, the engine will now use FArchiveAsync2 in the editor to be able to open them.

	(re-implemented CL #4400327 from Dev-Core)

	#jira UE-64389

Change 4404901 by Robert.Manuszewski

	Fixed a hang when entering PIE when Background Streaming is disabled

	#jira UE-63320

Change 4404816 by Ben.Marsh

	Fix "Non-void function should return a value" warning in CIS.

	#jira

Change 4404621 by Stefan.Boberg

	Re-enabled LLM on Editor builds since the performance issue has been addressed

	#jira UE-60020

Change 4404491 by Max.Chen

	Sequencer: Use cached object bindings when drawing motion trails.

	FindObjectId can be slow because it clears the cached bindings. Added FindCachedObjectId which retrieves the binding without clearing the cache.

	#jira UE-63347

Change 4404490 by Max.Chen

	Sequencer: Fix key reduction not working on imported fbx channel.

	Implement channel Optimize() so that it follows the curve editor's SimplifySelection by evaluating whether a key is needed (rather than removing the key and checking whether the value has changed).

	The main fix is that the tangents need to be calculated as non-normalized (ie. with the DisplayRate)

	#jira UE-62353

Change 4404488 by Max.Chen

	Sequencer: Changed HandleSingleNode fallback check for collapsed parents.

	#jira UE-64394

Change 4404486 by Max.Chen

	Sequencer: Remove restriction on hidden mobility. It's too wide to restrict transform tracks.

	#jira UE-63415

Change 4404472 by Max.Chen

	Sequencer: Restore drawing paths for selected nodes and channels

	#jira UE-64204

Change 4404468 by Max.Chen

	Sequencer: Check for property type in evaluation. This fixes an issue where the property type is changed after the track has been created.

	#jira UE-64278

Change 4404439 by Keli.Hlodversson

	Merging critical SteamVR fixes from Dev-VR
	#jira UE-63198
	#jira UE-64098
	#jira UEVR-1250

Change 4403717 by Marc.Audy

	Inline templated version of GetGameInstance

	#jira UE-62591

Change 4403702 by Marc.Audy

	PR #4944: Added template cast versions of multiple methods. (Contributed by francoap)
	#jira UE-62591

Change 4403699 by Marc.Audy

	Make reregistering an object in the significance manager an ensure not a check
	#jira

Change 4403697 by Marc.Audy

	PR #5074: Fixed minor bugs in GameUserSettings. (Contributed by phoenxin)
	#jira UE-63893

Change 4403695 by Marc.Audy

	Fix crash pasting timeline with external curve that has been force deleted
	#jira UE-64504

Change 4403693 by Marc.Audy

	PR #5077: Prevent TimelineComponent Update delegate from being executed 3 times when looping (Contributed by nedwardsnae)
	#jira UE-63927

Change 4403691 by Marc.Audy

	PR #5076: Correct icon when dragging an event dispatcher variable (Contributed by projectgheist)
	#jira UE-63895

Change 4403689 by Marc.Audy

	PR #5075: Mouse decorator when dragging a category from My Blueprint panel (Contributed by projectgheist)
	#jira UE-63894

Change 4403686 by Marc.Audy

	PR #5096: UE-64019: Use compare function for tooltip FText instead of != operator (Contributed by projectgheist)
	#jira UE-64109
	#jira UE-64019

Change 4403662 by Marc.Audy

	Ensure that objects created via duplication in PIE are correctly constructed
	#jira UE-63505

Change 4403462 by tim.gautier

	Cleaned up M_Tree_FallTreeMerged_01, added comment blocks for UEQATC-212
	#jira UE-29618

Change 4403308 by Michael.Trepka

	Update mouse position on NSDraggingUpdate event on Mac

	#jira UE-60800

Change 4403290 by Michael.Trepka

	Fixed issues with window position and size when toggling between fullscreen and windowed modes

	#jira UE-63212

Change 4403270 by Michael.Trepka

	Fixed a problem with mouse click/drag events being received by incorrect window/widget in certain conditions while dragging windows on Mac

	#jira UE-57506

Change 4403216 by Ben.Marsh

	Fix null reference exception when resolving paths.

	#jira

Change 4403213 by Ben.Marsh

	Revert change to use FileReference for executable to use in the <Spawn> task, to ensure we can use programs in the PATH.

	#jira

Change 4403209 by Ben.Marsh

	Prevent null dereference when tagging a set of files.

	#jira

Change 4403200 by Ben.Marsh

	BuildGraph: Add support for automatic parsing of collection types in BuildGraph parameters.

	#jira

Change 4403188 by Mikey.Boyd

	Adding content for Data Table test coverage.

	#jira UE-29618

Change 4403163 by Ben.Marsh

	Sanitize the contents of all the receipts in the installed engine build, to make sure they don't reference any files we don't distribute.

	#jira UE-56424

Change 4403151 by Ben.Marsh

	UBT: Don't assume that console encoding is UTF-8 when using ParallelExecutor.

	#jira UE-64248

Change 4403136 by Ben.Marsh

	Fix IWYU issues with SQLite support.

	#jira UE-64523

Change 4403116 by Ben.Marsh

	UBT: Fix project files being generated for VS2017 if the user only has AutoSDKs, and does not have the IDE installed.

	#jira UE-62726

Change 4403074 by Ben.Marsh

	UBT: Fix a couple of edge cases when parsing output from child processes.

	* If a single line was larger than 32kb, the process would be treated as having terminated.
	* If the last output line did not have a trailing newline, the line would be dropped.

	#jira UE-64234

Change 4403066 by Ben.Marsh

	Fix project browser reopening when creating a new project, if other command line arguments are present. Missing a space between the project file and previous argument list.

	#jira UE-63724


#lockdown Nick.Penwarden
#rb none

#ROBOMERGE-OWNER: jason.bestimt
#ROBOMERGE-AUTHOR: ben.marsh
#ROBOMERGE-SOURCE: CL 4447546 in //UE4/Main/...
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)

[CL 4452228 by jason bestimt in Dev-VR branch]
2018-10-11 11:48:32 -04:00

4512 lines
137 KiB
C++

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "NavigationSystem.h"
#include "Misc/ScopeLock.h"
#include "Stats/StatsMisc.h"
#include "Modules/ModuleManager.h"
#include "AI/Navigation/NavAgentInterface.h"
#include "Engine/World.h"
#include "GameFramework/Controller.h"
#include "AI/Navigation/NavRelevantInterface.h"
#include "UObject/UObjectIterator.h"
#include "EngineUtils.h"
#include "Logging/MessageLog.h"
#include "NavAreas/NavArea.h"
#include "NavigationOctree.h"
#include "VisualLogger/VisualLogger.h"
#include "NavMesh/NavMeshBoundsVolume.h"
#include "NavigationInvokerComponent.h"
#include "AI/Navigation/NavigationDataChunk.h"
#include "Engine/Engine.h"
#include "UObject/Package.h"
#include "Components/PrimitiveComponent.h"
#include "UObject/UObjectThreadContext.h"
#if WITH_RECAST
#include "NavMesh/RecastNavMesh.h"
#include "NavMesh/RecastHelpers.h"
#include "NavMesh/RecastNavMeshGenerator.h"
#endif // WITH_RECAST
#if WITH_EDITOR
#include "EditorModeManager.h"
#include "EditorModes.h"
#include "Editor/GeometryMode/Public/GeometryEdMode.h"
#include "Editor/GeometryMode/Public/EditorGeometry.h"
#endif
#if WITH_HOT_RELOAD
#include "Misc/HotReloadInterface.h"
#endif
#include "NavAreas/NavArea_Null.h"
#include "NavAreas/NavArea_Obstacle.h"
#include "NavAreas/NavArea_Default.h"
#include "NavAreas/NavAreaMeta_SwitchByAgent.h"
#include "NavLinkCustomInterface.h"
#include "NavigationPath.h"
#include "AbstractNavData.h"
#include "CrowdManagerBase.h"
static const uint32 INITIAL_ASYNC_QUERIES_SIZE = 32;
static const uint32 REGISTRATION_QUEUE_SIZE = 16; // and we'll not reallocate
#define LOCTEXT_NAMESPACE "Navigation"
DEFINE_LOG_CATEGORY_STATIC(LogNavOctree, Warning, All);
DECLARE_CYCLE_STAT(TEXT("Rasterize triangles"), STAT_Navigation_RasterizeTriangles,STATGROUP_Navigation);
DECLARE_CYCLE_STAT(TEXT("Nav Tick: area register"), STAT_Navigation_TickNavAreaRegister, STATGROUP_Navigation);
DECLARE_CYCLE_STAT(TEXT("Nav Tick: mark dirty"), STAT_Navigation_TickMarkDirty, STATGROUP_Navigation);
DECLARE_CYCLE_STAT(TEXT("Nav Tick: async build"), STAT_Navigation_TickAsyncBuild, STATGROUP_Navigation);
DECLARE_CYCLE_STAT(TEXT("Nav Tick: async pathfinding"), STAT_Navigation_TickAsyncPathfinding, STATGROUP_Navigation);
DECLARE_CYCLE_STAT(TEXT("Debug NavOctree Time"), STAT_DebugNavOctree, STATGROUP_Navigation);
//----------------------------------------------------------------------//
// Stats
//----------------------------------------------------------------------//
DEFINE_STAT(STAT_Navigation_QueriesTimeSync);
DEFINE_STAT(STAT_Navigation_RequestingAsyncPathfinding);
DEFINE_STAT(STAT_Navigation_PathfindingSync);
DEFINE_STAT(STAT_Navigation_PathfindingAsync);
DEFINE_STAT(STAT_Navigation_AddGeneratedTiles);
DEFINE_STAT(STAT_Navigation_TileNavAreaSorting);
DEFINE_STAT(STAT_Navigation_TileGeometryExportToObjAsync);
DEFINE_STAT(STAT_Navigation_TileVoxelFilteringAsync);
DEFINE_STAT(STAT_Navigation_TileBuildAsync);
DEFINE_STAT(STAT_Navigation_TileBuildPreparationSync);
DEFINE_STAT(STAT_Navigation_BSPExportSync);
DEFINE_STAT(STAT_Navigation_GatheringNavigationModifiersSync);
DEFINE_STAT(STAT_Navigation_ActorsGeometryExportSync);
DEFINE_STAT(STAT_Navigation_ProcessingActorsForNavMeshBuilding);
DEFINE_STAT(STAT_Navigation_AdjustingNavLinks);
DEFINE_STAT(STAT_Navigation_AddingActorsToNavOctree);
DEFINE_STAT(STAT_Navigation_RecastTick);
DEFINE_STAT(STAT_Navigation_RecastPathfinding);
DEFINE_STAT(STAT_Navigation_RecastBuildCompressedLayers);
DEFINE_STAT(STAT_Navigation_RecastBuildNavigation);
DEFINE_STAT(STAT_Navigation_UpdateNavOctree);
DEFINE_STAT(STAT_Navigation_CollisionTreeMemory);
DEFINE_STAT(STAT_Navigation_NavDataMemory);
DEFINE_STAT(STAT_Navigation_TileCacheMemory);
DEFINE_STAT(STAT_Navigation_OutOfNodesPath);
DEFINE_STAT(STAT_Navigation_PartialPath);
DEFINE_STAT(STAT_Navigation_CumulativeBuildTime);
DEFINE_STAT(STAT_Navigation_BuildTime);
DEFINE_STAT(STAT_Navigation_OffsetFromCorners);
DEFINE_STAT(STAT_Navigation_PathVisibilityOptimisation);
DEFINE_STAT(STAT_Navigation_ObservedPathsCount);
DEFINE_STAT(STAT_Navigation_RecastMemory);
CSV_DEFINE_CATEGORY(NAV_SYSTEM, true);
//----------------------------------------------------------------------//
// consts
//----------------------------------------------------------------------//
namespace FNavigationSystem
{
FORCEINLINE bool IsValidExtent(const FVector& Extent)
{
return Extent != INVALID_NAVEXTENT;
}
FCustomLinkOwnerInfo::FCustomLinkOwnerInfo(INavLinkCustomInterface* Link)
{
LinkInterface = Link;
LinkOwner = Link->GetLinkOwner();
}
bool ShouldLoadNavigationOnClient(ANavigationData& NavData)
{
const UWorld* World = NavData.GetWorld();
if (World && World->GetNavigationSystem())
{
const UNavigationSystemV1* NavSys = Cast<UNavigationSystemV1>(World->GetNavigationSystem());
return NavSys && NavSys->ShouldLoadNavigationOnClient(&NavData);
}
else
{
const UNavigationSystemV1* NavSysCDO = (*GEngine->NavigationSystemClass != nullptr)
? (GEngine->NavigationSystemClass->GetDefaultObject<const UNavigationSystemV1>())
: (const UNavigationSystemV1*)nullptr;
return NavSysCDO && NavSysCDO->ShouldLoadNavigationOnClient(&NavData);
}
}
bool ShouldDiscardSubLevelNavData(ANavigationData& NavData)
{
const UWorld* World = NavData.GetWorld();
if (World && World->GetNavigationSystem())
{
const UNavigationSystemV1* NavSys = Cast<UNavigationSystemV1>(World->GetNavigationSystem());
if (NavSys)
{
return NavSys->ShouldDiscardSubLevelNavData(&NavData);
}
}
const UNavigationSystemV1* NavSysCDO = (*GEngine->NavigationSystemClass != nullptr)
? (GEngine->NavigationSystemClass->GetDefaultObject<const UNavigationSystemV1>())
: (const UNavigationSystemV1*)nullptr;
return NavSysCDO == nullptr || NavSysCDO->ShouldDiscardSubLevelNavData(&NavData);
}
void MakeAllComponentsNeverAffectNav(AActor& Actor)
{
const TSet<UActorComponent*> Components = Actor.GetComponents();
for (UActorComponent* ActorComp : Components)
{
ActorComp->SetCanEverAffectNavigation(false);
}
}
}
namespace NavigationDebugDrawing
{
const float PathLineThickness = 3.f;
const FVector PathOffset(0,0,15);
const FVector PathNodeBoxExtent(16.f);
}
//----------------------------------------------------------------------//
// FNavigationInvoker
//----------------------------------------------------------------------//
FNavigationInvoker::FNavigationInvoker()
: Actor(nullptr)
, GenerationRadius(0)
, RemovalRadius(0)
{
}
FNavigationInvoker::FNavigationInvoker(AActor& InActor, float InGenerationRadius, float InRemovalRadius)
: Actor(&InActor)
, GenerationRadius(InGenerationRadius)
, RemovalRadius(InRemovalRadius)
{
}
//----------------------------------------------------------------------//
// helpers
//----------------------------------------------------------------------//
namespace
{
#if ENABLE_VISUAL_LOG
void NavigationDataDump(const UObject* Object, const FName& CategoryName, const ELogVerbosity::Type Verbosity, const FBox& Box, const UWorld& World, FVisualLogEntry& CurrentEntry)
{
const ANavigationData* MainNavData = FNavigationSystem::GetCurrent<UNavigationSystemV1>(&World)->GetDefaultNavDataInstance();
const FNavDataGenerator* Generator = MainNavData ? MainNavData->GetGenerator() : nullptr;
if (Generator)
{
Generator->GrabDebugSnapshot(&CurrentEntry, FMath::IsNearlyZero(Box.GetVolume()) ? MainNavData->GetBounds().ExpandBy(FVector(20, 20, 20)) : Box, CategoryName, Verbosity);
}
}
#endif // ENABLE_VISUAL_LOG
}
//----------------------------------------------------------------------//
// UNavigationSystemV1
//----------------------------------------------------------------------//
bool UNavigationSystemV1::bNavigationAutoUpdateEnabled = true;
TMap<INavLinkCustomInterface*, FWeakObjectPtr> UNavigationSystemV1::PendingCustomLinkRegistration;
FCriticalSection UNavigationSystemV1::CustomLinkRegistrationSection;
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
FNavigationSystemExec UNavigationSystemV1::ExecHandler;
#endif // !UE_BUILD_SHIPPING
/** called after navigation influencing event takes place*/
UNavigationSystemV1::FOnNavigationDirty UNavigationSystemV1::NavigationDirtyEvent;
bool UNavigationSystemV1::bUpdateNavOctreeOnComponentChange = true;
bool UNavigationSystemV1::bStaticRuntimeNavigation = false;
bool UNavigationSystemV1::bIsPIEActive = false;
//----------------------------------------------------------------------//
// life cycle stuff
//----------------------------------------------------------------------//
UNavigationSystemV1::UNavigationSystemV1(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, bTickWhilePaused(false)
, bWholeWorldNavigable(false)
, bSkipAgentHeightCheckWhenPickingNavData(false)
, DirtyAreasUpdateFreq(60)
, OperationMode(FNavigationSystemRunMode::InvalidMode)
, NavOctree(NULL)
, NavBuildingLockFlags(0)
, InitialNavBuildingLockFlags(0)
, bNavOctreeLock(false)
, bInitialSetupHasBeenPerformed(false)
, bInitialLevelsAdded(false)
, bWorldInitDone(false)
, CurrentlyDrawnNavDataIndex(0)
, DirtyAreasUpdateTime(0)
{
#if WITH_EDITOR
NavUpdateLockFlags = 0;
#endif
struct FDelegatesInitializer
{
FDelegatesInitializer()
{
UNavigationSystemBase::UpdateActorDataDelegate().BindStatic(&UNavigationSystemV1::UpdateActorInNavOctree);
UNavigationSystemBase::UpdateComponentDataDelegate().BindStatic(&UNavigationSystemV1::UpdateComponentInNavOctree);
UNavigationSystemBase::UpdateComponentDataAfterMoveDelegate().BindLambda([](USceneComponent& Comp) { UNavigationSystemV1::UpdateNavOctreeAfterMove(&Comp); });
UNavigationSystemBase::OnActorBoundsChangedDelegate().BindLambda([](AActor& Actor) { UNavigationSystemV1::UpdateNavOctreeBounds(&Actor); });
UNavigationSystemBase::OnPostEditActorMoveDelegate().BindLambda([](AActor& Actor) {
// update actor and all its components in navigation system after finishing move
// USceneComponent::UpdateNavigationData works only in game world
UNavigationSystemV1::UpdateNavOctreeBounds(&Actor);
TArray<AActor*> ParentedActors;
Actor.GetAttachedActors(ParentedActors);
for (int32 Idx = 0; Idx < ParentedActors.Num(); Idx++)
{
UNavigationSystemV1::UpdateNavOctreeBounds(ParentedActors[Idx]);
}
// not doing manual update of all attached actors since UpdateActorAndComponentsInNavOctree should take care of it
UNavigationSystemV1::UpdateActorAndComponentsInNavOctree(Actor);
});
UNavigationSystemBase::OnComponentTransformChangedDelegate().BindLambda([](USceneComponent& Comp) {
if (UNavigationSystemV1::ShouldUpdateNavOctreeOnComponentChange())
{
UWorld* World = Comp.GetWorld();
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys != nullptr
&& (NavSys->ShouldAllowClientSideNavigation() || !World->IsNetMode(ENetMode::NM_Client)))
{
// use propagated component's transform update in editor OR server game with additional navsys check
UNavigationSystemV1::UpdateNavOctreeAfterMove(&Comp);
}
}
});
UNavigationSystemBase::OnActorRegisteredDelegate().BindLambda([](AActor& Actor) { UNavigationSystemV1::OnActorRegistered(&Actor); });
UNavigationSystemBase::OnActorUnregisteredDelegate().BindLambda([](AActor& Actor) { UNavigationSystemV1::OnActorUnregistered(&Actor); });
UNavigationSystemBase::OnComponentRegisteredDelegate().BindLambda([](UActorComponent& Comp) { UNavigationSystemV1::OnComponentRegistered(&Comp); });
UNavigationSystemBase::OnComponentUnregisteredDelegate().BindLambda([](UActorComponent& Comp) { UNavigationSystemV1::OnComponentUnregistered(&Comp); });
UNavigationSystemBase::RemoveActorDataDelegate().BindLambda([](AActor& Actor) { UNavigationSystemV1::ClearNavOctreeAll(&Actor); });
UNavigationSystemBase::HasComponentDataDelegate().BindLambda([](UActorComponent& Comp) {
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(Comp.GetWorld());
return (NavSys && (NavSys->GetObjectsNavOctreeId(Comp) || NavSys->HasPendingObjectNavOctreeId(&Comp)));
});
UNavigationSystemBase::GetDefaultSupportedAgentDelegate().BindStatic(&UNavigationSystemV1::GetDefaultSupportedAgent);
UNavigationSystemBase::UpdateActorAndComponentDataDelegate().BindStatic(&UNavigationSystemV1::UpdateActorAndComponentsInNavOctree);
UNavigationSystemBase::OnComponentBoundsChangedDelegate().BindLambda([](UActorComponent& Comp, const FBox& NewBounds, const FBox& DirtyArea) {
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(Comp.GetWorld());
if (NavSys)
{
NavSys->UpdateNavOctreeElementBounds(&Comp, NewBounds, DirtyArea);
}
});
//UNavigationSystemBase::GetNavDataForPropsDelegate();
UNavigationSystemBase::GetNavDataForActorDelegate().BindStatic(&UNavigationSystemV1::GetNavDataForActor);
#if WITH_RECAST
UNavigationSystemBase::GetDefaultNavDataClassDelegate().BindLambda([]() { return ARecastNavMesh::StaticClass(); });
#endif // WITH_RECAST
UNavigationSystemBase::VerifyNavigationRenderingComponentsDelegate().BindLambda([](UWorld& World, const bool bShow) {
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(&World);
if (NavSys)
{
NavSys->VerifyNavigationRenderingComponents(bShow);
}
});
UNavigationSystemBase::BuildDelegate().BindLambda([](UWorld& World) {
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(&World);
if (NavSys)
{
NavSys->Build();
}
});
#if WITH_EDITOR
UNavigationSystemBase::OnPIEStartDelegate().BindLambda([](UWorld& World) {
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(&World);
if (NavSys)
{
NavSys->OnPIEStart();
}
});
UNavigationSystemBase::OnPIEEndDelegate().BindLambda([](UWorld& World) {
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(&World);
if (NavSys)
{
NavSys->OnPIEEnd();
}
});
UNavigationSystemBase::UpdateLevelCollisionDelegate().BindLambda([](ULevel& Level) {
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(&Level);
if (NavSys)
{
NavSys->UpdateLevelCollision(&Level);
}
});
UNavigationSystemBase::SetNavigationAutoUpdateEnableDelegate().BindStatic(&UNavigationSystemV1::SetNavigationAutoUpdateEnabled);
/*.BindLambda([](const bool bNewEnable, UNavigationSystemBase* InNavigationSystem) {
})*/
UNavigationSystemBase::AddNavigationUpdateLockDelegate().BindLambda([](UWorld& World, uint8 Flags) {
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(&World);
if (NavSys)
{
NavSys->AddNavigationUpdateLock(Flags);
}
});
UNavigationSystemBase::RemoveNavigationUpdateLockDelegate().BindLambda([](UWorld& World, uint8 Flags) {
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(&World);
if (NavSys)
{
NavSys->RemoveNavigationUpdateLock(Flags);
}
});
#endif // WITH_EDITOR
#if ENABLE_VISUAL_LOG
FVisualLogger::NavigationDataDumpDelegate.AddStatic(&NavigationDataDump);
#endif // ENABLE_VISUAL_LOG
}
};
static FDelegatesInitializer DelegatesInitializer;
// @hack, trying to load AIModule's CrowdManager
UClass* Class = StaticLoadClass(UCrowdManagerBase::StaticClass(), nullptr, TEXT("/Script/AIModule.CrowdManager"));
CrowdManagerClass = Class ? Class : UCrowdManagerBase::StaticClass();
// active tiles
NextInvokersUpdateTime = 0.f;
ActiveTilesUpdateInterval = 1.f;
bGenerateNavigationOnlyAroundNavigationInvokers = false;
DataGatheringMode = ENavDataGatheringModeConfig::Instant;
bCanAccumulateDirtyAreas = true;
bShouldDiscardSubLevelNavData = true;
#if !UE_BUILD_SHIPPING
bDirtyAreasReportedWhileAccumulationLocked = false;
#endif // !UE_BUILD_SHIPPING
if (HasAnyFlags(RF_ClassDefaultObject) == false)
{
// reserve some arbitrary size
AsyncPathFindingQueries.Reserve( INITIAL_ASYNC_QUERIES_SIZE );
NavDataRegistrationQueue.Reserve( REGISTRATION_QUEUE_SIZE );
FWorldDelegates::LevelAddedToWorld.AddUObject(this, &UNavigationSystemV1::OnLevelAddedToWorld);
FWorldDelegates::LevelRemovedFromWorld.AddUObject(this, &UNavigationSystemV1::OnLevelRemovedFromWorld);
#if !UE_BUILD_SHIPPING
FCoreDelegates::OnGetOnScreenMessages.AddUObject(this, &UNavigationSystemV1::GetOnScreenMessages);
#endif // !UE_BUILD_SHIPPING
}
else if (GetClass() == UNavigationSystemV1::StaticClass())
{
SetDefaultWalkableArea(UNavArea_Default::StaticClass());
SetDefaultObstacleArea(UNavArea_Obstacle::StaticClass());
const FTransform RecastToUnrealTransfrom(Recast2UnrealMatrix());
SetCoordTransformFrom(ENavigationCoordSystem::Recast, RecastToUnrealTransfrom);
}
#if WITH_EDITOR
if (GIsEditor && HasAnyFlags(RF_ClassDefaultObject) == false)
{
FEditorDelegates::EditorModeEnter.AddUObject(this, &UNavigationSystemV1::OnEditorModeChanged, true);
FEditorDelegates::EditorModeExit.AddUObject(this, &UNavigationSystemV1::OnEditorModeChanged, false);
}
#endif // WITH_EDITOR
}
UNavigationSystemV1::~UNavigationSystemV1()
{
CleanUp(FNavigationSystem::ECleanupMode::CleanupUnsafe);
#if WITH_EDITOR
if (GIsEditor)
{
FEditorDelegates::EditorModeEnter.RemoveAll(this);
FEditorDelegates::EditorModeExit.RemoveAll(this);
}
#endif // WITH_EDITOR
#if !UE_BUILD_SHIPPING
FCoreDelegates::OnGetOnScreenMessages.RemoveAll(this);
#endif // !UE_BUILD_SHIPPING
}
void UNavigationSystemV1::ConfigureAsStatic()
{
bStaticRuntimeNavigation = true;
SetWantsComponentChangeNotifies(false);
}
void UNavigationSystemV1::SetUpdateNavOctreeOnComponentChange(bool bNewUpdateOnComponentChange)
{
bUpdateNavOctreeOnComponentChange = bNewUpdateOnComponentChange;
}
void UNavigationSystemV1::DoInitialSetup()
{
if (bInitialSetupHasBeenPerformed)
{
return;
}
UpdateAbstractNavData();
CreateCrowdManager();
bInitialSetupHasBeenPerformed = true;
}
void UNavigationSystemV1::UpdateAbstractNavData()
{
if (AbstractNavData != nullptr && !AbstractNavData->IsPendingKill())
{
return;
}
// spawn abstract nav data separately
// it's responsible for direct paths and shouldn't be picked for any agent type as default one
UWorld* NavWorld = GetWorld();
for (TActorIterator<AAbstractNavData> It(NavWorld); It; ++It)
{
AAbstractNavData* Nav = (*It);
if (Nav && !Nav->IsPendingKill())
{
AbstractNavData = Nav;
break;
}
}
if (AbstractNavData == NULL)
{
FNavDataConfig DummyConfig;
DummyConfig.NavigationDataClass = AAbstractNavData::StaticClass();
AbstractNavData = CreateNavigationDataInstance(DummyConfig);
if (AbstractNavData)
{
AbstractNavData->SetFlags(RF_Transient);
}
}
}
void UNavigationSystemV1::SetSupportedAgentsNavigationClass(int32 AgentIndex, TSubclassOf<ANavigationData> NavigationDataClass)
{
check(SupportedAgents.IsValidIndex(AgentIndex));
SupportedAgents[AgentIndex].NavigationDataClass = NavigationDataClass;
// keep preferred navigation data class in sync with actual class
// this will be passed to navigation data actor and will be required
// for comparisons done in DoesSupportAgent calls
//
// "Any" navigation data preference is valid only for instanced agents
SupportedAgents[AgentIndex].SetPreferredNavData(NavigationDataClass);
if (NavigationDataClass != nullptr)
{
SupportedAgents[AgentIndex].NavigationDataClassName = FSoftClassPath::GetOrCreateIDForClass(NavigationDataClass);
}
else
{
SupportedAgents[AgentIndex].NavigationDataClassName.Reset();
}
#if WITH_EDITOR
if (GIsEditor)
{
if (HasAnyFlags(RF_ClassDefaultObject) == false)
{
// set it at CDO to properly show up in project settings
// @hack the reason for doing it this way is that engine doesn't handle default TSubclassOf properties
// set to game-specific classes;
UNavigationSystemV1* NavigationSystemCDO = GetMutableDefault<UNavigationSystemV1>(GetClass());
NavigationSystemCDO->SetSupportedAgentsNavigationClass(AgentIndex, NavigationDataClass);
}
}
#endif // WITH_EDITOR
}
void UNavigationSystemV1::PostInitProperties()
{
Super::PostInitProperties();
if (HasAnyFlags(RF_ClassDefaultObject) == false)
{
// Populate our NavAreaClasses list with all known nav area classes.
// If more are loaded after this they will be registered as they come
TArray<UClass*> CurrentNavAreaClasses;
GetDerivedClasses(UNavArea::StaticClass(), CurrentNavAreaClasses);
for (UClass* NavAreaClass : CurrentNavAreaClasses)
{
RegisterNavAreaClass(NavAreaClass);
}
// make sure there's at least one supported navigation agent size
if (SupportedAgents.Num() == 0)
{
SupportedAgents.Add(FNavDataConfig(FNavigationSystem::FallbackAgentRadius, FNavigationSystem::FallbackAgentHeight));
}
else
{
for (int32 AgentIndex = 0; AgentIndex < SupportedAgents.Num(); ++AgentIndex)
{
FNavDataConfig& SupportedAgentConfig = SupportedAgents[AgentIndex];
// a piece of legacy maintanance
if (SupportedAgentConfig.NavigationDataClass != nullptr && SupportedAgentConfig.NavigationDataClassName.IsValid() == false)
{
// fill NavigationDataClassName
SupportedAgentConfig.NavigationDataClassName = FSoftClassPath(SupportedAgentConfig.NavigationDataClass);
}
else
{
TSubclassOf<ANavigationData> NavigationDataClass = SupportedAgentConfig.NavigationDataClassName.IsValid()
? LoadClass<ANavigationData>(NULL, *SupportedAgentConfig.NavigationDataClassName.ToString(), NULL, LOAD_None, NULL)
: nullptr;
SetSupportedAgentsNavigationClass(AgentIndex, NavigationDataClass);
}
}
}
if (bInitialBuildingLocked)
{
InitialNavBuildingLockFlags |= ENavigationBuildLock::InitialLock;
}
uint8 UseLockFlags = InitialNavBuildingLockFlags;
AddNavigationBuildLock(UseLockFlags);
// register for any actor move change
#if WITH_EDITOR
if ( GIsEditor )
{
GEngine->OnActorMoved().AddUObject(this, &UNavigationSystemV1::OnActorMoved);
}
#endif
FCoreUObjectDelegates::PostLoadMapWithWorld.AddUObject(this, &UNavigationSystemV1::OnPostLoadMap);
UNavigationSystemV1::NavigationDirtyEvent.AddUObject(this, &UNavigationSystemV1::OnNavigationDirtied);
#if WITH_HOT_RELOAD
IHotReloadInterface& HotReloadSupport = FModuleManager::LoadModuleChecked<IHotReloadInterface>("HotReload");
HotReloadDelegateHandle = HotReloadSupport.OnHotReload().AddUObject(this, &UNavigationSystemV1::OnHotReload);
#endif
}
}
bool UNavigationSystemV1::ConditionalPopulateNavOctree()
{
// Discard all navigation updates caused by octree construction
TGuardValue<TArray<FNavigationDirtyArea>> DirtyGuard(DirtyAreas, TArray<FNavigationDirtyArea>());
// We are going to fully re-populate NavOctree so all pending update request are outdated
PendingOctreeUpdates.Empty(32);
// Discard current octree
DestroyNavOctree();
// See if any of registered navigation data need navoctree
bSupportRebuilding = RequiresNavOctree();
if (bSupportRebuilding)
{
NavOctree = MakeShareable(new FNavigationOctree(FVector(0,0,0), 64000));
NavOctree->SetDataGatheringMode(DataGatheringMode);
const ERuntimeGenerationType RuntimeGenerationType = GetRuntimeGenerationType();
const bool bStoreNavGeometry = (RuntimeGenerationType == ERuntimeGenerationType::Dynamic);
NavOctree->SetNavigableGeometryStoringMode(bStoreNavGeometry ? FNavigationOctree::StoreNavGeometry : FNavigationOctree::SkipNavGeometry);
if (bStoreNavGeometry)
{
#if WITH_RECAST
NavOctree->ComponentExportDelegate = FNavigationOctree::FNavigableGeometryComponentExportDelegate::CreateStatic(&FRecastNavMeshGenerator::ExportComponentGeometry);
#endif // WITH_RECAST
}
if (!IsNavigationOctreeLocked())
{
UWorld* World = GetWorld();
check(World);
// now process all actors on all levels
for (int32 LevelIndex = 0; LevelIndex < World->GetNumLevels(); ++LevelIndex)
{
ULevel* Level = World->GetLevel(LevelIndex);
AddLevelCollisionToOctree(Level);
for (int32 ActorIndex = 0; ActorIndex < Level->Actors.Num(); ActorIndex++)
{
AActor* Actor = Level->Actors[ActorIndex];
const bool bLegalActor = Actor && !Actor->IsPendingKill();
if (bLegalActor)
{
UpdateActorAndComponentsInNavOctree(*Actor);
}
}
}
}
}
// Add all found elements to octree, this will not add new dirty areas to navigation
if (PendingOctreeUpdates.Num())
{
for (TSet<FNavigationDirtyElement>::TIterator It(PendingOctreeUpdates); It; ++It)
{
AddElementToNavOctree(*It);
}
PendingOctreeUpdates.Empty(32);
}
return bSupportRebuilding;
}
#if WITH_EDITOR
void UNavigationSystemV1::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent)
{
static const FName NAME_NavigationDataClass = GET_MEMBER_NAME_CHECKED(FNavDataConfig, NavigationDataClass);
static const FName NAME_SupportedAgents = GET_MEMBER_NAME_CHECKED(UNavigationSystemV1, SupportedAgents);
static const FName NAME_AllowClientSideNavigation = GET_MEMBER_NAME_CHECKED(UNavigationSystemV1, bAllowClientSideNavigation);
Super::PostEditChangeChainProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property)
{
FName PropName = PropertyChangedEvent.Property->GetFName();
if (PropName == NAME_NavigationDataClass)
{
int32 SupportedAgentIndex = PropertyChangedEvent.GetArrayIndex(NAME_SupportedAgents.ToString());
if (SupportedAgents.IsValidIndex(SupportedAgentIndex))
{
// reflect the change to SupportedAgent's
TSubclassOf<ANavigationData> NavClass = SupportedAgents[SupportedAgentIndex].NavigationDataClass.Get();
SetSupportedAgentsNavigationClass(SupportedAgentIndex, NavClass);
SaveConfig();
}
}
else if (PropName == NAME_AllowClientSideNavigation && HasAnyFlags(RF_ClassDefaultObject))
{
for (FObjectIterator It(UNavigationSystemModuleConfig::StaticClass()); It; ++It)
{
((UNavigationSystemModuleConfig*)*It)->UpdateWithNavSysCDO(*this);
}
}
}
}
void UNavigationSystemV1::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
static const FName NAME_EnableActiveTiles = GET_MEMBER_NAME_CHECKED(UNavigationSystemV1, bGenerateNavigationOnlyAroundNavigationInvokers);
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property)
{
FName PropName = PropertyChangedEvent.Property->GetFName();
if (PropName == NAME_EnableActiveTiles)
{
if (NavOctree.IsValid())
{
NavOctree->SetDataGatheringMode(DataGatheringMode);
}
for (auto NavData : NavDataSet)
{
if (NavData)
{
NavData->RestrictBuildingToActiveTiles(bGenerateNavigationOnlyAroundNavigationInvokers);
}
}
}
}
}
#endif // WITH_EDITOR
void UNavigationSystemV1::OnInitializeActors()
{
}
void UNavigationSystemV1::OnWorldInitDone(FNavigationSystemRunMode Mode)
{
static const bool bSkipRebuildInEditor = true;
OperationMode = Mode;
DoInitialSetup();
UWorld* World = GetWorld();
if (IsThereAnywhereToBuildNavigation() == false
// Simulation mode is a special case - better not do it in this case
&& OperationMode != FNavigationSystemRunMode::SimulationMode)
{
// remove all navigation data instances
for (TActorIterator<ANavigationData> It(World); It; ++It)
{
ANavigationData* Nav = (*It);
if (Nav != NULL && Nav->IsPendingKill() == false && Nav != GetAbstractNavData())
{
UnregisterNavData(Nav);
Nav->CleanUpAndMarkPendingKill();
bNavDataRemovedDueToMissingNavBounds = true;
}
}
if (OperationMode == FNavigationSystemRunMode::EditorMode)
{
RemoveNavigationBuildLock(InitialNavBuildingLockFlags, bSkipRebuildInEditor);
}
}
else
{
// Discard all bounds updates that was submitted during world initialization,
// to avoid navigation rebuild right after map is loaded
PendingNavBoundsUpdates.Empty();
// gather navigable bounds
GatherNavigationBounds();
// gather all navigation data instances and register all not-yet-registered
// (since it's quite possible navigation system was not ready by the time
// those instances were serialized-in or spawned)
RegisterNavigationDataInstances();
if (bAutoCreateNavigationData == true)
{
SpawnMissingNavigationData();
// in case anything spawned has registered
ProcessRegistrationCandidates();
}
else
{
const bool bIsBuildLocked = IsNavigationBuildingLocked();
if (GetDefaultNavDataInstance(FNavigationSystem::DontCreate) != NULL)
{
// trigger navmesh update
for (TActorIterator<ANavigationData> It(World); It; ++It)
{
ANavigationData* NavData = (*It);
if (NavData != NULL)
{
ERegistrationResult Result = RegisterNavData(NavData);
if (Result == RegistrationSuccessful)
{
if (!bIsBuildLocked && bNavigationAutoUpdateEnabled)
{
NavData->RebuildAll();
}
}
else if (Result != RegistrationFailed_DataPendingKill
&& Result != RegistrationFailed_AgentNotValid
)
{
NavData->CleanUpAndMarkPendingKill();
}
}
}
}
}
if (OperationMode == FNavigationSystemRunMode::EditorMode)
{
// don't lock navigation building in editor
RemoveNavigationBuildLock(InitialNavBuildingLockFlags, bSkipRebuildInEditor);
}
// See if any of registered navigation data needs NavOctree
ConditionalPopulateNavOctree();
// All navigation actors are registered
// Add NavMesh parts from all sub-levels that were streamed in prior NavMesh registration
const auto& Levels = World->GetLevels();
for (ULevel* Level : Levels)
{
if (!Level->IsPersistentLevel() && Level->bIsVisible)
{
for (ANavigationData* NavData : NavDataSet)
{
if (NavData)
{
NavData->OnStreamingLevelAdded(Level, World);
}
}
}
}
}
if (Mode == FNavigationSystemRunMode::EditorMode)
{
#if WITH_EDITOR
// make sure this static get applied to this instance
bNavigationAutoUpdateEnabled = !bNavigationAutoUpdateEnabled;
SetNavigationAutoUpdateEnabled(!bNavigationAutoUpdateEnabled, this);
#endif
// update navigation invokers
if (bGenerateNavigationOnlyAroundNavigationInvokers)
{
for (TObjectIterator<UNavigationInvokerComponent> It; It; ++It)
{
if (World == It->GetWorld())
{
It->RegisterWithNavigationSystem(*this);
}
}
}
// update navdata after loading world
if (bNavigationAutoUpdateEnabled)
{
const bool bIsLoadTime = true;
RebuildAll(bIsLoadTime);
}
}
if (!bCanAccumulateDirtyAreas)
{
DirtyAreas.Empty();
}
bWorldInitDone = true;
OnNavigationInitDone.Broadcast();
}
void UNavigationSystemV1::RegisterNavigationDataInstances()
{
UWorld* World = GetWorld();
bool bProcessRegistration = false;
for (TActorIterator<ANavigationData> It(World); It; ++It)
{
ANavigationData* Nav = (*It);
if (Nav != NULL && Nav->IsPendingKill() == false && Nav->IsRegistered() == false)
{
RequestRegistration(Nav, false);
bProcessRegistration = true;
}
}
if (bProcessRegistration == true)
{
ProcessRegistrationCandidates();
}
}
void UNavigationSystemV1::CreateCrowdManager()
{
if (CrowdManagerClass)
{
SetCrowdManager(NewObject<UCrowdManagerBase>(this, CrowdManagerClass));
}
}
void UNavigationSystemV1::SetCrowdManager(UCrowdManagerBase* NewCrowdManager)
{
if (NewCrowdManager == CrowdManager.Get())
{
return;
}
if (CrowdManager.IsValid())
{
CrowdManager->RemoveFromRoot();
}
CrowdManager = NewCrowdManager;
if (NewCrowdManager != NULL)
{
CrowdManager->AddToRoot();
}
}
void UNavigationSystemV1::Tick(float DeltaSeconds)
{
SET_DWORD_STAT(STAT_Navigation_ObservedPathsCount, 0);
UWorld* World = GetWorld();
if (World == nullptr
|| (bTickWhilePaused == false && World->IsPaused())
#if WITH_EDITOR
|| (bIsPIEActive && !World->IsGameWorld())
#endif // WITH_EDITOR
)
{
return;
}
const bool bIsGame = World->IsGameWorld();
if (PendingCustomLinkRegistration.Num())
{
ProcessCustomLinkPendingRegistration();
}
if (PendingNavBoundsUpdates.Num() > 0)
{
PerformNavigationBoundsUpdate(PendingNavBoundsUpdates);
PendingNavBoundsUpdates.Reset();
}
if (PendingOctreeUpdates.Num() > 0)
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_AddingActorsToNavOctree);
SCOPE_CYCLE_COUNTER(STAT_Navigation_BuildTime)
STAT(double ThisTime = 0);
{
SCOPE_SECONDS_COUNTER(ThisTime);
for (TSet<FNavigationDirtyElement>::TIterator It(PendingOctreeUpdates); It; ++It)
{
AddElementToNavOctree(*It);
}
PendingOctreeUpdates.Empty(32);
}
INC_FLOAT_STAT_BY(STAT_Navigation_CumulativeBuildTime,(float)ThisTime*1000);
}
if (bGenerateNavigationOnlyAroundNavigationInvokers)
{
UpdateInvokers();
}
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_TickMarkDirty);
DirtyAreasUpdateTime += DeltaSeconds;
const float DirtyAreasUpdateDeltaTime = 1.0f / DirtyAreasUpdateFreq;
const bool bCanRebuildNow = (DirtyAreasUpdateTime >= DirtyAreasUpdateDeltaTime) || !bIsGame;
const bool bIsLocked = IsNavigationBuildingLocked();
if (DirtyAreas.Num() > 0 && bCanRebuildNow && !bIsLocked)
{
for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex)
{
ANavigationData* NavData = NavDataSet[NavDataIndex];
if (NavData)
{
NavData->RebuildDirtyAreas(DirtyAreas);
}
}
DirtyAreasUpdateTime = 0;
DirtyAreas.Reset();
}
}
// Tick navigation mesh async builders
if (!bAsyncBuildPaused)
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_TickAsyncBuild);
CSV_SCOPED_TIMING_STAT(NAV_SYSTEM, Navigation_TickAsyncBuild);
for (ANavigationData* NavData : NavDataSet)
{
if (NavData)
{
NavData->TickAsyncBuild(DeltaSeconds);
}
}
}
if (AsyncPathFindingQueries.Num() > 0)
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_TickAsyncPathfinding);
TriggerAsyncQueries(AsyncPathFindingQueries);
AsyncPathFindingQueries.Reset();
}
if (CrowdManager.IsValid())
{
CrowdManager->Tick(DeltaSeconds);
}
}
void UNavigationSystemV1::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
{
UNavigationSystemV1* This = CastChecked<UNavigationSystemV1>(InThis);
UCrowdManagerBase* CrowdManager = This->GetCrowdManager();
Collector.AddReferencedObject(CrowdManager, InThis);
// don't reference NavAreaClasses in editor (unless PIE is active)
if (This->OperationMode != FNavigationSystemRunMode::EditorMode)
{
Collector.AddReferencedObjects(This->NavAreaClasses, InThis);
}
}
#if WITH_EDITOR
void UNavigationSystemV1::SetNavigationAutoUpdateEnabled(bool bNewEnable, UNavigationSystemBase* InNavigationSystemBase)
{
if (bNewEnable != bNavigationAutoUpdateEnabled)
{
bNavigationAutoUpdateEnabled = bNewEnable;
UNavigationSystemV1* NavSystem = Cast<UNavigationSystemV1>(InNavigationSystemBase);
if (NavSystem)
{
NavSystem->bCanAccumulateDirtyAreas = bNavigationAutoUpdateEnabled
|| (NavSystem->OperationMode != FNavigationSystemRunMode::EditorMode && NavSystem->OperationMode != FNavigationSystemRunMode::InvalidMode);
if (bNavigationAutoUpdateEnabled)
{
const bool bSkipRebuildsInEditor = false;
NavSystem->RemoveNavigationBuildLock(ENavigationBuildLock::NoUpdateInEditor, bSkipRebuildsInEditor);
}
else
{
#if !UE_BUILD_SHIPPING
NavSystem->bDirtyAreasReportedWhileAccumulationLocked = false;
#endif // !UE_BUILD_SHIPPING
NavSystem->AddNavigationBuildLock(ENavigationBuildLock::NoUpdateInEditor);
}
}
}
}
#endif // WITH_EDITOR
//----------------------------------------------------------------------//
// Public querying interface
//----------------------------------------------------------------------//
FPathFindingResult UNavigationSystemV1::FindPathSync(const FNavAgentProperties& AgentProperties, FPathFindingQuery Query, EPathFindingMode::Type Mode)
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_PathfindingSync);
if (Query.NavData.IsValid() == false)
{
Query.NavData = GetNavDataForProps(AgentProperties);
}
FPathFindingResult Result(ENavigationQueryResult::Error);
if (Query.NavData.IsValid())
{
if (Mode == EPathFindingMode::Hierarchical)
{
Result = Query.NavData->FindHierarchicalPath(AgentProperties, Query);
}
else
{
Result = Query.NavData->FindPath(AgentProperties, Query);
}
}
return Result;
}
FPathFindingResult UNavigationSystemV1::FindPathSync(FPathFindingQuery Query, EPathFindingMode::Type Mode)
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_PathfindingSync);
if (Query.NavData.IsValid() == false)
{
Query.NavData = GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
}
FPathFindingResult Result(ENavigationQueryResult::Error);
if (Query.NavData.IsValid())
{
if (Mode == EPathFindingMode::Regular)
{
Result = Query.NavData->FindPath(Query.NavAgentProperties, Query);
}
else // EPathFindingMode::Hierarchical
{
Result = Query.NavData->FindHierarchicalPath(Query.NavAgentProperties, Query);
}
}
return Result;
}
bool UNavigationSystemV1::TestPathSync(FPathFindingQuery Query, EPathFindingMode::Type Mode, int32* NumVisitedNodes) const
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_PathfindingSync);
if (Query.NavData.IsValid() == false)
{
Query.NavData = GetDefaultNavDataInstance();
}
bool bExists = false;
if (Query.NavData.IsValid())
{
if (Mode == EPathFindingMode::Hierarchical)
{
bExists = Query.NavData->TestHierarchicalPath(Query.NavAgentProperties, Query, NumVisitedNodes);
}
else
{
bExists = Query.NavData->TestPath(Query.NavAgentProperties, Query, NumVisitedNodes);
}
}
return bExists;
}
void UNavigationSystemV1::AddAsyncQuery(const FAsyncPathFindingQuery& Query)
{
check(IsInGameThread());
AsyncPathFindingQueries.Add(Query);
}
uint32 UNavigationSystemV1::FindPathAsync(const FNavAgentProperties& AgentProperties, FPathFindingQuery Query, const FNavPathQueryDelegate& ResultDelegate, EPathFindingMode::Type Mode)
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_RequestingAsyncPathfinding);
if (Query.NavData.IsValid() == false)
{
Query.NavData = GetNavDataForProps(AgentProperties);
}
if (Query.NavData.IsValid())
{
FAsyncPathFindingQuery AsyncQuery(Query, ResultDelegate, Mode);
if (AsyncQuery.QueryID != INVALID_NAVQUERYID)
{
AddAsyncQuery(AsyncQuery);
}
return AsyncQuery.QueryID;
}
return INVALID_NAVQUERYID;
}
void UNavigationSystemV1::AbortAsyncFindPathRequest(uint32 AsynPathQueryID)
{
check(IsInGameThread());
FAsyncPathFindingQuery* Query = AsyncPathFindingQueries.GetData();
for (int32 Index = 0; Index < AsyncPathFindingQueries.Num(); ++Index, ++Query)
{
if (Query->QueryID == AsynPathQueryID)
{
AsyncPathFindingQueries.RemoveAtSwap(Index);
break;
}
}
}
FAutoConsoleTaskPriority CPrio_TriggerAsyncQueries(
TEXT("TaskGraph.TaskPriorities.NavTriggerAsyncQueries"),
TEXT("Task and thread priority for UNavigationSystemV1::PerformAsyncQueries."),
ENamedThreads::BackgroundThreadPriority, // if we have background priority task threads, then use them...
ENamedThreads::NormalTaskPriority, // .. at normal task priority
ENamedThreads::NormalTaskPriority // if we don't have background threads, then use normal priority threads at normal task priority instead
);
void UNavigationSystemV1::TriggerAsyncQueries(TArray<FAsyncPathFindingQuery>& PathFindingQueries)
{
DECLARE_CYCLE_STAT(TEXT("FSimpleDelegateGraphTask.NavigationSystem batched async queries"),
STAT_FSimpleDelegateGraphTask_NavigationSystemBatchedAsyncQueries,
STATGROUP_TaskGraphTasks);
FSimpleDelegateGraphTask::CreateAndDispatchWhenReady(
FSimpleDelegateGraphTask::FDelegate::CreateUObject(this, &UNavigationSystemV1::PerformAsyncQueries, PathFindingQueries),
GET_STATID(STAT_FSimpleDelegateGraphTask_NavigationSystemBatchedAsyncQueries), nullptr, CPrio_TriggerAsyncQueries.Get());
}
static void AsyncQueryDone(FAsyncPathFindingQuery Query)
{
Query.OnDoneDelegate.ExecuteIfBound(Query.QueryID, Query.Result.Result, Query.Result.Path);
}
void UNavigationSystemV1::PerformAsyncQueries(TArray<FAsyncPathFindingQuery> PathFindingQueries)
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_PathfindingAsync);
if (PathFindingQueries.Num() == 0)
{
return;
}
for (FAsyncPathFindingQuery& Query : PathFindingQueries)
{
// @todo this is not necessarily the safest way to use UObjects outside of main thread.
// think about something else.
const ANavigationData* NavData = Query.NavData.IsValid() ? Query.NavData.Get() : GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
// perform query
if (NavData)
{
if (Query.Mode == EPathFindingMode::Hierarchical)
{
Query.Result = NavData->FindHierarchicalPath(Query.NavAgentProperties, Query);
}
else
{
Query.Result = NavData->FindPath(Query.NavAgentProperties, Query);
}
}
else
{
Query.Result = ENavigationQueryResult::Error;
}
// @todo make it return more informative results (bResult == false)
// trigger calling delegate on main thread - otherwise it may depend too much on stuff being thread safe
DECLARE_CYCLE_STAT(TEXT("FSimpleDelegateGraphTask.Async nav query finished"),
STAT_FSimpleDelegateGraphTask_AsyncNavQueryFinished,
STATGROUP_TaskGraphTasks);
FSimpleDelegateGraphTask::CreateAndDispatchWhenReady(
FSimpleDelegateGraphTask::FDelegate::CreateStatic(AsyncQueryDone, Query),
GET_STATID(STAT_FSimpleDelegateGraphTask_AsyncNavQueryFinished), NULL, ENamedThreads::GameThread);
}
}
bool UNavigationSystemV1::GetRandomPoint(FNavLocation& ResultLocation, ANavigationData* NavData, FSharedConstNavQueryFilter QueryFilter)
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_QueriesTimeSync);
if (NavData == NULL)
{
NavData = MainNavData;
}
if (NavData != NULL)
{
ResultLocation = NavData->GetRandomPoint(QueryFilter);
return true;
}
return false;
}
bool UNavigationSystemV1::GetRandomReachablePointInRadius(const FVector& Origin, float Radius, FNavLocation& ResultLocation, ANavigationData* NavData, FSharedConstNavQueryFilter QueryFilter) const
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_QueriesTimeSync);
if (NavData == nullptr)
{
NavData = MainNavData;
}
return NavData != nullptr && NavData->GetRandomReachablePointInRadius(Origin, Radius, ResultLocation, QueryFilter);
}
bool UNavigationSystemV1::GetRandomPointInNavigableRadius(const FVector& Origin, float Radius, FNavLocation& ResultLocation, ANavigationData* NavData, FSharedConstNavQueryFilter QueryFilter) const
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_QueriesTimeSync);
if (NavData == nullptr)
{
NavData = MainNavData;
}
return NavData != nullptr && NavData->GetRandomPointInNavigableRadius(Origin, Radius, ResultLocation, QueryFilter);
}
ENavigationQueryResult::Type UNavigationSystemV1::GetPathCost(const FVector& PathStart, const FVector& PathEnd, float& OutPathCost, const ANavigationData* NavData, FSharedConstNavQueryFilter QueryFilter) const
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_QueriesTimeSync);
if (NavData == NULL)
{
NavData = GetDefaultNavDataInstance();
}
return NavData != NULL ? NavData->CalcPathCost(PathStart, PathEnd, OutPathCost, QueryFilter) : ENavigationQueryResult::Error;
}
ENavigationQueryResult::Type UNavigationSystemV1::GetPathLength(const FVector& PathStart, const FVector& PathEnd, float& OutPathLength, const ANavigationData* NavData, FSharedConstNavQueryFilter QueryFilter) const
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_QueriesTimeSync);
if (NavData == NULL)
{
NavData = GetDefaultNavDataInstance();
}
return NavData != NULL ? NavData->CalcPathLength(PathStart, PathEnd, OutPathLength, QueryFilter) : ENavigationQueryResult::Error;
}
ENavigationQueryResult::Type UNavigationSystemV1::GetPathLengthAndCost(const FVector& PathStart, const FVector& PathEnd, float& OutPathLength, float& OutPathCost, const ANavigationData* NavData, FSharedConstNavQueryFilter QueryFilter) const
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_QueriesTimeSync);
if (NavData == NULL)
{
NavData = GetDefaultNavDataInstance();
}
return NavData != NULL ? NavData->CalcPathLengthAndCost(PathStart, PathEnd, OutPathLength, OutPathCost, QueryFilter) : ENavigationQueryResult::Error;
}
bool UNavigationSystemV1::ProjectPointToNavigation(const FVector& Point, FNavLocation& OutLocation, const FVector& Extent, const ANavigationData* NavData, FSharedConstNavQueryFilter QueryFilter) const
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_QueriesTimeSync);
if (NavData == NULL)
{
NavData = GetDefaultNavDataInstance();
}
return NavData != NULL && NavData->ProjectPoint(Point, OutLocation
, FNavigationSystem::IsValidExtent(Extent) ? Extent : NavData->GetConfig().DefaultQueryExtent
, QueryFilter);
}
UNavigationPath* UNavigationSystemV1::FindPathToActorSynchronously(UObject* WorldContextObject, const FVector& PathStart, AActor* GoalActor, float TetherDistance, AActor* PathfindingContext, TSubclassOf<UNavigationQueryFilter> FilterClass)
{
if (GoalActor == NULL)
{
return NULL;
}
INavAgentInterface* NavAgent = Cast<INavAgentInterface>(GoalActor);
UNavigationPath* GeneratedPath = FindPathToLocationSynchronously(WorldContextObject, PathStart, NavAgent ? NavAgent->GetNavAgentLocation() : GoalActor->GetActorLocation(), PathfindingContext, FilterClass);
if (GeneratedPath != NULL && GeneratedPath->GetPath().IsValid() == true)
{
GeneratedPath->GetPath()->SetGoalActorObservation(*GoalActor, TetherDistance);
}
return GeneratedPath;
}
UNavigationPath* UNavigationSystemV1::FindPathToLocationSynchronously(UObject* WorldContextObject, const FVector& PathStart, const FVector& PathEnd, AActor* PathfindingContext, TSubclassOf<UNavigationQueryFilter> FilterClass)
{
UWorld* World = NULL;
if (WorldContextObject != NULL)
{
World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
}
if (World == NULL && PathfindingContext != NULL)
{
World = GEngine->GetWorldFromContextObject(PathfindingContext, EGetWorldErrorMode::LogAndReturnNull);
}
UNavigationPath* ResultPath = NULL;
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys != nullptr && NavSys->GetDefaultNavDataInstance() != nullptr)
{
ResultPath = NewObject<UNavigationPath>(NavSys);
bool bValidPathContext = false;
const ANavigationData* NavigationData = NULL;
if (PathfindingContext != NULL)
{
INavAgentInterface* NavAgent = Cast<INavAgentInterface>(PathfindingContext);
if (NavAgent != NULL)
{
const FNavAgentProperties& AgentProps = NavAgent->GetNavAgentPropertiesRef();
NavigationData = NavSys->GetNavDataForProps(AgentProps);
bValidPathContext = true;
}
else if (Cast<ANavigationData>(PathfindingContext))
{
NavigationData = (ANavigationData*)PathfindingContext;
bValidPathContext = true;
}
}
if (bValidPathContext == false)
{
// just use default
NavigationData = NavSys->GetDefaultNavDataInstance();
}
check(NavigationData);
const FPathFindingQuery Query(PathfindingContext, *NavigationData, PathStart, PathEnd, UNavigationQueryFilter::GetQueryFilter(*NavigationData, PathfindingContext, FilterClass));
const FPathFindingResult Result = NavSys->FindPathSync(Query, EPathFindingMode::Regular);
if (Result.IsSuccessful())
{
ResultPath->SetPath(Result.Path);
}
}
return ResultPath;
}
bool UNavigationSystemV1::NavigationRaycast(UObject* WorldContextObject, const FVector& RayStart, const FVector& RayEnd, FVector& HitLocation, TSubclassOf<UNavigationQueryFilter> FilterClass, AController* Querier)
{
UWorld* World = NULL;
if (WorldContextObject != NULL)
{
World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
}
if (World == NULL && Querier != NULL)
{
World = GEngine->GetWorldFromContextObject(Querier, EGetWorldErrorMode::LogAndReturnNull);
}
// blocked, i.e. not traversable, by default
bool bRaycastBlocked = true;
HitLocation = RayStart;
const UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys)
{
// figure out which navigation data to use
const ANavigationData* NavData = NULL;
INavAgentInterface* MyNavAgent = Cast<INavAgentInterface>(Querier);
if (MyNavAgent)
{
const FNavAgentProperties& AgentProps = MyNavAgent->GetNavAgentPropertiesRef();
NavData = NavSys->GetNavDataForProps(AgentProps);
}
if (NavData == NULL)
{
NavData = NavSys->GetDefaultNavDataInstance();
}
if (NavData != NULL)
{
bRaycastBlocked = NavData->Raycast(RayStart, RayEnd, HitLocation, UNavigationQueryFilter::GetQueryFilter(*NavData, Querier, FilterClass));
}
}
return bRaycastBlocked;
}
void UNavigationSystemV1::GetNavAgentPropertiesArray(TArray<FNavAgentProperties>& OutNavAgentProperties) const
{
AgentToNavDataMap.GetKeys(OutNavAgentProperties);
}
ANavigationData* UNavigationSystemV1::GetNavDataForProps(const FNavAgentProperties& AgentProperties)
{
const UNavigationSystemV1* ConstThis = AsConst(this);
return const_cast<ANavigationData*>(ConstThis->GetNavDataForProps(AgentProperties));
}
// @todo could optimize this by having "SupportedAgentIndex" in FNavAgentProperties
const ANavigationData* UNavigationSystemV1::GetNavDataForProps(const FNavAgentProperties& AgentProperties) const
{
if (SupportedAgents.Num() <= 1)
{
return MainNavData;
}
const TWeakObjectPtr<ANavigationData>* NavDataForAgent = AgentToNavDataMap.Find(AgentProperties);
const ANavigationData* NavDataInstance = NavDataForAgent ? NavDataForAgent->Get() : nullptr;
if (NavDataInstance == nullptr)
{
TArray<FNavAgentProperties> AgentPropertiesList;
AgentToNavDataMap.GenerateKeyArray(AgentPropertiesList);
FNavAgentProperties BestFitNavAgent;
float BestExcessHeight = -FLT_MAX;
float BestExcessRadius = -FLT_MAX;
float ExcessRadius = -FLT_MAX;
float ExcessHeight = -FLT_MAX;
const float AgentHeight = bSkipAgentHeightCheckWhenPickingNavData ? 0.f : AgentProperties.AgentHeight;
for (TArray<FNavAgentProperties>::TConstIterator It(AgentPropertiesList); It; ++It)
{
const FNavAgentProperties& NavIt = *It;
const bool bNavClassMatch = NavIt.IsNavDataMatching(AgentProperties);
if (!bNavClassMatch)
{
continue;
}
ExcessRadius = NavIt.AgentRadius - AgentProperties.AgentRadius;
ExcessHeight = bSkipAgentHeightCheckWhenPickingNavData ? 0.f : (NavIt.AgentHeight - AgentHeight);
const bool bExcessRadiusIsBetter = ((ExcessRadius == 0) && (BestExcessRadius != 0))
|| ((ExcessRadius > 0) && (BestExcessRadius < 0))
|| ((ExcessRadius > 0) && (BestExcessRadius > 0) && (ExcessRadius < BestExcessRadius))
|| ((ExcessRadius < 0) && (BestExcessRadius < 0) && (ExcessRadius > BestExcessRadius));
const bool bExcessHeightIsBetter = ((ExcessHeight == 0) && (BestExcessHeight != 0))
|| ((ExcessHeight > 0) && (BestExcessHeight < 0))
|| ((ExcessHeight > 0) && (BestExcessHeight > 0) && (ExcessHeight < BestExcessHeight))
|| ((ExcessHeight < 0) && (BestExcessHeight < 0) && (ExcessHeight > BestExcessHeight));
const bool bBestIsValid = (BestExcessRadius >= 0) && (BestExcessHeight >= 0);
const bool bRadiusEquals = (ExcessRadius == BestExcessRadius);
const bool bHeightEquals = (ExcessHeight == BestExcessHeight);
bool bValuesAreBest = ((bExcessRadiusIsBetter || bRadiusEquals) && (bExcessHeightIsBetter || bHeightEquals));
if (!bValuesAreBest && !bBestIsValid)
{
bValuesAreBest = bExcessRadiusIsBetter || (bRadiusEquals && bExcessHeightIsBetter);
}
if (bValuesAreBest)
{
BestFitNavAgent = NavIt;
BestExcessHeight = ExcessHeight;
BestExcessRadius = ExcessRadius;
}
}
if (BestFitNavAgent.IsValid())
{
NavDataForAgent = AgentToNavDataMap.Find(BestFitNavAgent);
NavDataInstance = NavDataForAgent ? NavDataForAgent->Get() : nullptr;
}
}
return NavDataInstance ? NavDataInstance : MainNavData;
}
ANavigationData* UNavigationSystemV1::GetDefaultNavDataInstance(FNavigationSystem::ECreateIfMissing CreateNewIfNoneFound)
{
checkSlow(IsInGameThread() == true);
if (MainNavData == NULL || MainNavData->IsPendingKill())
{
MainNavData = NULL;
// @TODO this should be done a differently. There should be specified a "default agent"
for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex)
{
ANavigationData* NavData = NavDataSet[NavDataIndex];
if (NavData && !NavData->IsPendingKill() && NavData->CanBeMainNavData())
{
MainNavData = NavData;
break;
}
}
#if WITH_RECAST
if ( /*GIsEditor && */(MainNavData == NULL) && CreateNewIfNoneFound == FNavigationSystem::Create )
{
// Spawn a new one if we're in the editor. In-game, either we loaded one or we don't get one.
MainNavData = GetWorld()->SpawnActor<ANavigationData>(ARecastNavMesh::StaticClass());
}
#endif // WITH_RECAST
// either way make sure it's registered. Registration stores unique
// navmeshes, so we have nothing to lose
RegisterNavData(MainNavData);
}
return MainNavData;
}
FSharedNavQueryFilter UNavigationSystemV1::CreateDefaultQueryFilterCopy() const
{
return MainNavData ? MainNavData->GetDefaultQueryFilter()->GetCopy() : NULL;
}
bool UNavigationSystemV1::IsNavigationBuilt(const AWorldSettings* Settings) const
{
if (Settings == nullptr || Settings->IsNavigationSystemEnabled() == false || IsThereAnywhereToBuildNavigation() == false)
{
return true;
}
bool bIsBuilt = true;
for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex)
{
ANavigationData* NavData = NavDataSet[NavDataIndex];
if (NavData != NULL && NavData->GetWorldSettings() == Settings)
{
FNavDataGenerator* Generator = NavData->GetGenerator();
if ((NavData->GetRuntimeGenerationMode() != ERuntimeGenerationType::Static
#if WITH_EDITOR
|| GEditor != NULL
#endif // WITH_EDITOR
) && (Generator == NULL || Generator->IsBuildInProgress(/*bCheckDirtyToo=*/true) == true))
{
bIsBuilt = false;
break;
}
}
}
return bIsBuilt;
}
bool UNavigationSystemV1::IsThereAnywhereToBuildNavigation() const
{
// not check if there are any volumes or other structures requiring/supporting navigation building
if (bWholeWorldNavigable == true)
{
return true;
}
for (const FNavigationBounds& Bounds : RegisteredNavBounds)
{
if (Bounds.AreaBox.IsValid)
{
return true;
}
}
// @TODO this should be made more flexible to be able to trigger this from game-specific
// code (like Navigation System's subclass maybe)
bool bCreateNavigation = false;
for (TActorIterator<ANavMeshBoundsVolume> It(GetWorld()); It; ++It)
{
ANavMeshBoundsVolume const* const V = (*It);
if (V != NULL && !V->IsPendingKill())
{
bCreateNavigation = true;
break;
}
}
return bCreateNavigation;
}
bool UNavigationSystemV1::IsNavigationRelevant(const AActor* TestActor) const
{
const INavRelevantInterface* NavInterface = Cast<const INavRelevantInterface>(TestActor);
if (NavInterface && NavInterface->IsNavigationRelevant())
{
return true;
}
if (TestActor)
{
TInlineComponentArray<UActorComponent*> Components;
for (int32 Idx = 0; Idx < Components.Num(); Idx++)
{
NavInterface = Cast<const INavRelevantInterface>(Components[Idx]);
if (NavInterface && NavInterface->IsNavigationRelevant())
{
return true;
}
}
}
return false;
}
FBox UNavigationSystemV1::GetWorldBounds() const
{
checkSlow(IsInGameThread() == true);
NavigableWorldBounds = FBox(ForceInit);
if (GetWorld() != nullptr)
{
if (bWholeWorldNavigable == false)
{
for (const FNavigationBounds& Bounds : RegisteredNavBounds)
{
NavigableWorldBounds += Bounds.AreaBox;
}
}
else
{
// @TODO - super slow! Need to ask tech guys where I can get this from
for (FActorIterator It(GetWorld()); It; ++It)
{
if (IsNavigationRelevant(*It))
{
NavigableWorldBounds += (*It)->GetComponentsBoundingBox();
}
}
}
}
return NavigableWorldBounds;
}
FBox UNavigationSystemV1::GetLevelBounds(ULevel* InLevel) const
{
FBox NavigableLevelBounds(ForceInit);
if (InLevel)
{
AActor** Actor = InLevel->Actors.GetData();
const int32 ActorCount = InLevel->Actors.Num();
for (int32 ActorIndex = 0; ActorIndex < ActorCount; ++ActorIndex, ++Actor)
{
if (IsNavigationRelevant(*Actor))
{
NavigableLevelBounds += (*Actor)->GetComponentsBoundingBox();
}
}
}
return NavigableLevelBounds;
}
const TSet<FNavigationBounds>& UNavigationSystemV1::GetNavigationBounds() const
{
return RegisteredNavBounds;
}
void UNavigationSystemV1::ApplyWorldOffset(const FVector& InOffset, bool bWorldShift)
{
// Attempt at generation of new nav mesh after the shift
// dynamic navmesh, we regenerate completely
if (GetRuntimeGenerationType() == ERuntimeGenerationType::Dynamic)
{
//stop generators from building navmesh
CancelBuild();
ConditionalPopulateNavOctree();
Build();
for (ANavigationData* NavData : NavDataSet)
{
if (NavData)
{
NavData->ConditionalConstructGenerator();
ARecastNavMesh* RecastNavMesh = Cast<ARecastNavMesh>(NavData);
if (RecastNavMesh) RecastNavMesh->RequestDrawingUpdate();
}
}
}
else // static navmesh
{
//not sure what happens when we shift farther than the extents of the NavOctree are
for (ANavigationData* NavData : NavDataSet)
{
if (NavData)
{
NavData->ApplyWorldOffset(InOffset, bWorldShift);
}
}
}
}
//----------------------------------------------------------------------//
// Bookkeeping
//----------------------------------------------------------------------//
void UNavigationSystemV1::RequestRegistration(ANavigationData* NavData, bool bTriggerRegistrationProcessing)
{
FScopeLock RegistrationLock(&NavDataRegistrationSection);
if (NavDataRegistrationQueue.Num() < REGISTRATION_QUEUE_SIZE)
{
NavDataRegistrationQueue.AddUnique(NavData);
// checking if bWorldInitDone since requesting out-of-order registration
// processing when we're still setting up can result in odd cases,
// like initializing navmesh generators while the nav system doesn't have
// the navmesh bounds collected yet.
if (bTriggerRegistrationProcessing && bWorldInitDone)
{
// trigger registration candidates processing
DECLARE_CYCLE_STAT(TEXT("FSimpleDelegateGraphTask.Process registration candidates"),
STAT_FSimpleDelegateGraphTask_ProcessRegistrationCandidates,
STATGROUP_TaskGraphTasks);
FSimpleDelegateGraphTask::CreateAndDispatchWhenReady(
FSimpleDelegateGraphTask::FDelegate::CreateUObject(this, &UNavigationSystemV1::ProcessRegistrationCandidates),
GET_STATID(STAT_FSimpleDelegateGraphTask_ProcessRegistrationCandidates), NULL, ENamedThreads::GameThread);
}
}
else
{
UE_LOG(LogNavigation, Error, TEXT("Navigation System: registration queue full!"));
}
}
void UNavigationSystemV1::ProcessRegistrationCandidates()
{
//if (FUObjectThreadContext::Get().IsRoutingPostLoad)
//{
// // postopne
// return;
//}
FScopeLock RegistrationLock(&NavDataRegistrationSection);
if (NavDataRegistrationQueue.Num() == 0)
{
return;
}
ANavigationData** NavDataPtr = NavDataRegistrationQueue.GetData();
const int CandidatesCount = NavDataRegistrationQueue.Num();
for (int32 CandidateIndex = 0; CandidateIndex < CandidatesCount; ++CandidateIndex, ++NavDataPtr)
{
if (*NavDataPtr != NULL)
{
ERegistrationResult Result = RegisterNavData(*NavDataPtr);
if (Result == RegistrationSuccessful)
{
continue;
}
else if (Result != RegistrationFailed_DataPendingKill)
{
(*NavDataPtr)->CleanUpAndMarkPendingKill();
if ((*NavDataPtr) == MainNavData)
{
MainNavData = NULL;
}
}
}
}
MainNavData = GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
// we processed all candidates so clear the queue
NavDataRegistrationQueue.Reset();
}
void UNavigationSystemV1::ProcessCustomLinkPendingRegistration()
{
FScopeLock AccessLock(&CustomLinkRegistrationSection);
TMap<INavLinkCustomInterface*, FWeakObjectPtr> TempPending = PendingCustomLinkRegistration;
PendingCustomLinkRegistration.Empty();
for (TMap<INavLinkCustomInterface*, FWeakObjectPtr>::TIterator It(TempPending); It; ++It)
{
INavLinkCustomInterface* ILink = It.Key();
FWeakObjectPtr LinkOb = It.Value();
if (LinkOb.IsValid() && ILink)
{
RegisterCustomLink(*ILink);
}
}
}
UNavigationSystemV1::ERegistrationResult UNavigationSystemV1::RegisterNavData(ANavigationData* NavData)
{
if (NavData == NULL)
{
return RegistrationError;
}
else if (NavData->IsPendingKill() == true)
{
return RegistrationFailed_DataPendingKill;
}
// still to be seen if this is really true, but feels right
else if (NavData->IsRegistered() == true)
{
return RegistrationSuccessful;
}
FScopeLock Lock(&NavDataRegistration);
UNavigationSystemV1::ERegistrationResult Result = RegistrationError;
// find out which, if any, navigation agents are supported by this nav data
// if none then fail the registration
FNavDataConfig NavConfig = NavData->GetConfig();
// not discarding navmesh when there's only one Supported Agent
if (NavConfig.IsValid() == false && SupportedAgents.Num() == 1)
{
// fill in AgentProps with whatever is the instance's setup
NavConfig = SupportedAgents[0];
NavData->SetConfig(SupportedAgents[0]);
NavData->SetSupportsDefaultAgent(true);
NavData->ProcessNavAreas(NavAreaClasses, 0);
}
if (NavConfig.IsValid() == true)
{
// check if this kind of agent has already its navigation implemented
TWeakObjectPtr<ANavigationData>* NavDataForAgent = AgentToNavDataMap.Find(NavConfig);
ANavigationData* NavDataInstanceForAgent = NavDataForAgent ? NavDataForAgent->Get() : nullptr;
if (NavDataInstanceForAgent == nullptr)
{
if (NavData->IsA(AAbstractNavData::StaticClass()) == false)
{
// ok, so this navigation agent doesn't have its navmesh registered yet, but do we want to support it?
bool bAgentSupported = false;
for (int32 AgentIndex = 0; AgentIndex < SupportedAgents.Num(); ++AgentIndex)
{
if (NavData->GetClass() == SupportedAgents[AgentIndex].NavigationDataClass && SupportedAgents[AgentIndex].IsEquivalent(NavConfig) == true)
{
// it's supported, then just in case it's not a precise match (IsEquivalent succeeds with some precision)
// update NavData with supported Agent
bAgentSupported = true;
NavData->SetConfig(SupportedAgents[AgentIndex]);
AgentToNavDataMap.Add(SupportedAgents[AgentIndex], NavData);
NavData->SetSupportsDefaultAgent(AgentIndex == 0);
NavData->ProcessNavAreas(NavAreaClasses, AgentIndex);
OnNavDataRegisteredEvent.Broadcast(NavData);
NavDataSet.AddUnique(NavData);
NavData->OnRegistered();
break;
}
}
Result = bAgentSupported == true ? RegistrationSuccessful : RegistrationFailed_AgentNotValid;
}
else
{
// fake registration since it's a special navigation data type
// and it would get discarded for not implementing any particular
// navigation agent
// Node that we don't add abstract navigation data to NavDataSet
NavData->OnRegistered();
Result = RegistrationSuccessful;
}
}
else if (NavDataInstanceForAgent == NavData)
{
ensure(NavDataSet.Find(NavData) != INDEX_NONE);
// let's treat double registration of the same nav data with the same agent as a success
Result = RegistrationSuccessful;
}
else
{
// otherwise specified agent type already has its navmesh implemented, fail redundant instance
Result = RegistrationFailed_AgentAlreadySupported;
}
}
else
{
Result = RegistrationFailed_AgentNotValid;
}
// @todo else might consider modifying this NavData to implement navigation for one of the supported agents
// care needs to be taken to not make it implement navigation for agent who's real implementation has
// not been loaded yet.
return Result;
}
void UNavigationSystemV1::UnregisterNavData(ANavigationData* NavData)
{
NavDataSet.RemoveSingle(NavData);
if (NavData == NULL)
{
return;
}
FScopeLock Lock(&NavDataRegistration);
NavData->OnUnregistered();
}
void UNavigationSystemV1::RegisterCustomLink(INavLinkCustomInterface& CustomLink)
{
uint32 LinkId = CustomLink.GetLinkId();
// if there's already a link with that Id registered, assign new Id and mark dirty area
// this won't fix baked data in static navmesh (in game), but every other case will regenerate affected tiles
if (CustomLinksMap.Contains(LinkId))
{
LinkId = INavLinkCustomInterface::GetUniqueId();
CustomLink.UpdateLinkId(LinkId);
UObject* CustomLinkOb = CustomLink.GetLinkOwner();
UActorComponent* OwnerComp = Cast<UActorComponent>(CustomLinkOb);
AActor* OwnerActor = OwnerComp ? OwnerComp->GetOwner() : Cast<AActor>(CustomLinkOb);
if (OwnerActor)
{
ENavLinkDirection::Type DummyDir = ENavLinkDirection::BothWays;
FVector RelativePtA, RelativePtB;
CustomLink.GetLinkData(RelativePtA, RelativePtB, DummyDir);
const FTransform OwnerActorTM = OwnerActor->GetTransform();
const FVector WorldPtA = OwnerActorTM.TransformPosition(RelativePtA);
const FVector WorldPtB = OwnerActorTM.TransformPosition(RelativePtB);
FBox LinkBounds(ForceInitToZero);
LinkBounds += WorldPtA;
LinkBounds += WorldPtB;
AddDirtyArea(LinkBounds, OctreeUpdate_Modifiers);
}
}
CustomLinksMap.Add(LinkId, FNavigationSystem::FCustomLinkOwnerInfo(&CustomLink));
}
void UNavigationSystemV1::UnregisterCustomLink(INavLinkCustomInterface& CustomLink)
{
CustomLinksMap.Remove(CustomLink.GetLinkId());
}
INavLinkCustomInterface* UNavigationSystemV1::GetCustomLink(uint32 UniqueLinkId) const
{
const FNavigationSystem::FCustomLinkOwnerInfo* LinkInfo = CustomLinksMap.Find(UniqueLinkId);
return (LinkInfo && LinkInfo->IsValid()) ? LinkInfo->LinkInterface : nullptr;
}
void UNavigationSystemV1::UpdateCustomLink(const INavLinkCustomInterface* CustomLink)
{
for (TMap<FNavAgentProperties, TWeakObjectPtr<ANavigationData> >::TIterator It(AgentToNavDataMap); It; ++It)
{
ANavigationData* NavData = It.Value().Get();
if (NavData)
{
NavData->UpdateCustomLink(CustomLink);
}
}
}
void UNavigationSystemV1::RequestCustomLinkRegistering(INavLinkCustomInterface& CustomLink, UObject* OwnerOb)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(OwnerOb);
if (NavSys)
{
NavSys->RegisterCustomLink(CustomLink);
}
else
{
FScopeLock AccessLock(&CustomLinkRegistrationSection);
PendingCustomLinkRegistration.Add(&CustomLink, OwnerOb);
}
}
void UNavigationSystemV1::RequestCustomLinkUnregistering(INavLinkCustomInterface& CustomLink, UObject* OwnerOb)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(OwnerOb);
if (NavSys)
{
NavSys->UnregisterCustomLink(CustomLink);
}
else
{
FScopeLock AccessLock(&CustomLinkRegistrationSection);
PendingCustomLinkRegistration.Remove(&CustomLink);
}
}
void UNavigationSystemV1::RequestAreaUnregistering(UClass* NavAreaClass)
{
for (TObjectIterator<UNavigationSystemV1> NavSysIt; NavSysIt; ++NavSysIt)
{
NavSysIt->UnregisterNavAreaClass(NavAreaClass);
}
}
void UNavigationSystemV1::UnregisterNavAreaClass(UClass* NavAreaClass)
{
// remove from known areas
if (NavAreaClasses.Remove(NavAreaClass) > 0)
{
// notify navigation data
// notify existing nav data
OnNavigationAreaEvent(NavAreaClass, ENavAreaEvent::Unregistered);
}
}
void UNavigationSystemV1::RequestAreaRegistering(UClass* NavAreaClass)
{
for (TObjectIterator<UNavigationSystemV1> NavSysIt; NavSysIt; ++NavSysIt)
{
NavSysIt->RegisterNavAreaClass(NavAreaClass);
}
}
void UNavigationSystemV1::RegisterNavAreaClass(UClass* AreaClass)
{
// can't be null
if (AreaClass == NULL)
{
return;
}
// can't be abstract
if (AreaClass->HasAnyClassFlags(CLASS_Abstract))
{
return;
}
// special handling of blueprint based areas
if (AreaClass->HasAnyClassFlags(CLASS_CompiledFromBlueprint))
{
// can't be skeleton of blueprint class
if (AreaClass->GetName().Contains(TEXT("SKEL_")))
{
return;
}
// can't be class from Developers folder (won't be saved properly anyway)
const UPackage* Package = AreaClass->GetOutermost();
if (Package && Package->GetName().Contains(TEXT("/Developers/")))
{
return;
}
}
if (NavAreaClasses.Contains(AreaClass))
{
// Already added
return;
}
UNavArea* AreaClassCDO = GetMutableDefault<UNavArea>(AreaClass);
check(AreaClassCDO);
// initialize flags
AreaClassCDO->InitializeArea();
// add to know areas
NavAreaClasses.Add(AreaClass);
// notify existing nav data
OnNavigationAreaEvent(AreaClass, ENavAreaEvent::Registered);
#if WITH_EDITOR
UNavAreaMeta_SwitchByAgent* SwitchByAgentCDO = Cast<UNavAreaMeta_SwitchByAgent>(AreaClassCDO);
// update area properties
if (SwitchByAgentCDO)
{
SwitchByAgentCDO->UpdateAgentConfig();
}
#endif
}
void UNavigationSystemV1::OnNavigationAreaEvent(UClass* AreaClass, ENavAreaEvent::Type Event)
{
// notify existing nav data
for (auto NavigationData : NavDataSet)
{
if (NavigationData != NULL && NavigationData->IsPendingKillPending() == false)
{
NavigationData->OnNavAreaEvent(AreaClass, Event);
}
}
}
int32 UNavigationSystemV1::GetSupportedAgentIndex(const ANavigationData* NavData) const
{
if (SupportedAgents.Num() < 2)
{
return 0;
}
const FNavDataConfig& TestConfig = NavData->GetConfig();
for (int32 AgentIndex = 0; AgentIndex < SupportedAgents.Num(); AgentIndex++)
{
if (SupportedAgents[AgentIndex].IsEquivalent(TestConfig))
{
return AgentIndex;
}
}
return INDEX_NONE;
}
int32 UNavigationSystemV1::GetSupportedAgentIndex(const FNavAgentProperties& NavAgent) const
{
if (SupportedAgents.Num() < 2)
{
return 0;
}
for (int32 AgentIndex = 0; AgentIndex < SupportedAgents.Num(); AgentIndex++)
{
if (SupportedAgents[AgentIndex].IsEquivalent(NavAgent))
{
return AgentIndex;
}
}
return INDEX_NONE;
}
void UNavigationSystemV1::DescribeFilterFlags(UEnum* FlagsEnum) const
{
#if WITH_EDITOR
TArray<FString> FlagDesc;
FString EmptyStr;
FlagDesc.Init(EmptyStr, 16);
const int32 NumEnums = FMath::Min(16, FlagsEnum->NumEnums() - 1); // skip _MAX
for (int32 FlagIndex = 0; FlagIndex < NumEnums; FlagIndex++)
{
FlagDesc[FlagIndex] = FlagsEnum->GetDisplayNameTextByIndex(FlagIndex).ToString();
}
DescribeFilterFlags(FlagDesc);
#endif
}
void UNavigationSystemV1::DescribeFilterFlags(const TArray<FString>& FlagsDesc) const
{
#if WITH_EDITOR
const int32 MaxFlags = 16;
TArray<FString> UseDesc = FlagsDesc;
FString EmptyStr;
while (UseDesc.Num() < MaxFlags)
{
UseDesc.Add(EmptyStr);
}
// get special value from recast's navmesh
#if WITH_RECAST
uint16 NavLinkFlag = ARecastNavMesh::GetNavLinkFlag();
for (int32 FlagIndex = 0; FlagIndex < MaxFlags; FlagIndex++)
{
if ((NavLinkFlag >> FlagIndex) & 1)
{
UseDesc[FlagIndex] = TEXT("Navigation link");
break;
}
}
#endif
// setup properties
UStructProperty* StructProp1 = FindField<UStructProperty>(UNavigationQueryFilter::StaticClass(), TEXT("IncludeFlags"));
UStructProperty* StructProp2 = FindField<UStructProperty>(UNavigationQueryFilter::StaticClass(), TEXT("ExcludeFlags"));
check(StructProp1);
check(StructProp2);
UStruct* Structs[] = { StructProp1->Struct, StructProp2->Struct };
const FString CustomNameMeta = TEXT("DisplayName");
for (int32 StructIndex = 0; StructIndex < ARRAY_COUNT(Structs); StructIndex++)
{
for (int32 FlagIndex = 0; FlagIndex < MaxFlags; FlagIndex++)
{
FString PropName = FString::Printf(TEXT("bNavFlag%d"), FlagIndex);
UProperty* Prop = FindField<UProperty>(Structs[StructIndex], *PropName);
check(Prop);
if (UseDesc[FlagIndex].Len())
{
Prop->SetPropertyFlags(CPF_Edit);
Prop->SetMetaData(*CustomNameMeta, *UseDesc[FlagIndex]);
}
else
{
Prop->ClearPropertyFlags(CPF_Edit);
}
}
}
#endif
}
void UNavigationSystemV1::ResetCachedFilter(TSubclassOf<UNavigationQueryFilter> FilterClass)
{
for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); NavDataIndex++)
{
if (NavDataSet[NavDataIndex])
{
NavDataSet[NavDataIndex]->RemoveQueryFilter(FilterClass);
}
}
}
UNavigationSystemV1* UNavigationSystemV1::CreateNavigationSystem(UWorld* WorldOwner)
{
UNavigationSystemV1* NavSys = NULL;
// create navigation system for editor and server targets, but remove it from game clients
if (WorldOwner && (*GEngine->NavigationSystemClass != nullptr)
&& (GEngine->NavigationSystemClass->GetDefaultObject<UNavigationSystemV1>()->bAllowClientSideNavigation || WorldOwner->GetNetMode() != NM_Client))
{
AWorldSettings* WorldSettings = WorldOwner->GetWorldSettings();
if (WorldSettings == NULL || WorldSettings->IsNavigationSystemEnabled())
{
NavSys = NewObject<UNavigationSystemV1>(WorldOwner, GEngine->NavigationSystemClass);
WorldOwner->SetNavigationSystem(NavSys);
}
}
return NavSys;
}
void UNavigationSystemV1::InitializeForWorld(UWorld& World, FNavigationSystemRunMode Mode)
{
OnWorldInitDone(Mode);
}
UNavigationSystemV1* UNavigationSystemV1::GetCurrent(UWorld* World)
{
return FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
}
UNavigationSystemV1* UNavigationSystemV1::GetCurrent(UObject* WorldContextObject)
{
return FNavigationSystem::GetCurrent<UNavigationSystemV1>(WorldContextObject);
}
ANavigationData* UNavigationSystemV1::GetNavDataWithID(const uint16 NavDataID) const
{
for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex)
{
const ANavigationData* NavData = NavDataSet[NavDataIndex];
if (NavData != NULL && NavData->GetNavDataUniqueID() == NavDataID)
{
return const_cast<ANavigationData*>(NavData);
}
}
return NULL;
}
void UNavigationSystemV1::AddDirtyArea(const FBox& NewArea, int32 Flags)
{
if (Flags > 0 && bCanAccumulateDirtyAreas && NewArea.IsValid)
{
DirtyAreas.Add(FNavigationDirtyArea(NewArea, Flags));
}
#if !UE_BUILD_SHIPPING
bDirtyAreasReportedWhileAccumulationLocked = bDirtyAreasReportedWhileAccumulationLocked || (Flags > 0 && !bCanAccumulateDirtyAreas);
#endif // !UE_BUILD_SHIPPING
}
void UNavigationSystemV1::AddDirtyAreas(const TArray<FBox>& NewAreas, int32 Flags)
{
for (int32 NewAreaIndex = 0; NewAreaIndex < NewAreas.Num(); NewAreaIndex++)
{
AddDirtyArea(NewAreas[NewAreaIndex], Flags);
}
}
bool UNavigationSystemV1::HasDirtyAreasQueued() const
{
return DirtyAreas.Num() > 0;
}
int32 GetDirtyFlagHelper(int32 UpdateFlags, int32 DefaultValue)
{
return ((UpdateFlags & UNavigationSystemV1::OctreeUpdate_Geometry) != 0) ? ENavigationDirtyFlag::All :
((UpdateFlags & UNavigationSystemV1::OctreeUpdate_Modifiers) != 0) ? ENavigationDirtyFlag::DynamicModifier :
DefaultValue;
}
FSetElementId UNavigationSystemV1::RegisterNavOctreeElement(UObject* ElementOwner, INavRelevantInterface* ElementInterface, int32 UpdateFlags)
{
FSetElementId SetId;
#if WITH_EDITOR
if (IsNavigationRegisterLocked())
{
return SetId;
}
#endif
if (NavOctree.IsValid() == false || ElementOwner == NULL || ElementInterface == NULL)
{
return SetId;
}
if (IsNavigationOctreeLocked())
{
UE_LOG(LogNavOctree, Log, TEXT("IGNORE(RegisterNavOctreeElement) %s"), *GetPathNameSafe(ElementOwner));
return SetId;
}
const bool bIsRelevant = ElementInterface->IsNavigationRelevant();
UE_LOG(LogNavOctree, Log, TEXT("REG %s %s"), *GetNameSafe(ElementOwner), bIsRelevant ? TEXT("[relevant]") : TEXT(""));
if (bIsRelevant)
{
bool bCanAdd = false;
UObject* ParentNode = ElementInterface->GetNavigationParent();
if (ParentNode)
{
OctreeChildNodesMap.AddUnique(ParentNode, FWeakObjectPtr(ElementOwner));
bCanAdd = true;
}
else
{
const FOctreeElementId* ElementId = GetObjectsNavOctreeId(*ElementOwner);
bCanAdd = (ElementId == NULL);
}
if (bCanAdd)
{
FNavigationDirtyElement UpdateInfo(ElementOwner, ElementInterface, GetDirtyFlagHelper(UpdateFlags, 0));
SetId = PendingOctreeUpdates.FindId(UpdateInfo);
if (SetId.IsValidId())
{
// make sure this request stays, in case it has been invalidated already
PendingOctreeUpdates[SetId] = UpdateInfo;
}
else
{
SetId = PendingOctreeUpdates.Add(UpdateInfo);
}
}
}
return SetId;
}
void UNavigationSystemV1::AddElementToNavOctree(const FNavigationDirtyElement& DirtyElement)
{
// handle invalidated requests first
if (DirtyElement.bInvalidRequest)
{
if (DirtyElement.bHasPrevData)
{
AddDirtyArea(DirtyElement.PrevBounds, DirtyElement.PrevFlags);
}
return;
}
UObject* ElementOwner = DirtyElement.Owner.Get();
if (ElementOwner == NULL || ElementOwner->IsPendingKill() || DirtyElement.NavInterface == NULL)
{
return;
}
FNavigationOctreeElement GeneratedData(*ElementOwner);
const FBox ElementBounds = DirtyElement.NavInterface->GetNavigationBounds();
UObject* NavigationParent = DirtyElement.NavInterface->GetNavigationParent();
if (NavigationParent)
{
// check if parent node is waiting in queue
const FSetElementId ParentRequestId = PendingOctreeUpdates.FindId(FNavigationDirtyElement(NavigationParent));
const FOctreeElementId* ParentId = GetObjectsNavOctreeId(*NavigationParent);
if (ParentRequestId.IsValidId() && ParentId == NULL)
{
FNavigationDirtyElement& ParentNode = PendingOctreeUpdates[ParentRequestId];
AddElementToNavOctree(ParentNode);
// mark as invalid so it won't be processed twice
ParentNode.bInvalidRequest = true;
}
const FOctreeElementId* UseParentId = ParentId ? ParentId : GetObjectsNavOctreeId(*NavigationParent);
if (UseParentId && NavOctree->IsValidElementId(*UseParentId))
{
UE_LOG(LogNavOctree, Log, TEXT("ADD %s to %s"), *GetNameSafe(ElementOwner), *GetNameSafe(NavigationParent));
NavOctree->AppendToNode(*UseParentId, DirtyElement.NavInterface, ElementBounds, GeneratedData);
}
else
{
UE_LOG(LogNavOctree, Warning, TEXT("Can't add node [%s] - parent [%s] not found in octree!"), *GetNameSafe(ElementOwner), *GetNameSafe(NavigationParent));
}
}
else
{
UE_LOG(LogNavOctree, Log, TEXT("ADD %s"), *GetNameSafe(ElementOwner));
NavOctree->AddNode(ElementOwner, DirtyElement.NavInterface, ElementBounds, GeneratedData);
}
const FBox BBox = GeneratedData.Bounds.GetBox();
const bool bValidBBox = BBox.IsValid && !BBox.GetSize().IsNearlyZero();
if (bValidBBox && !GeneratedData.IsEmpty())
{
const int32 DirtyFlag = DirtyElement.FlagsOverride ? DirtyElement.FlagsOverride : GeneratedData.Data->GetDirtyFlag();
AddDirtyArea(BBox, DirtyFlag);
}
}
bool UNavigationSystemV1::GetNavOctreeElementData(const UObject& NodeOwner, int32& DirtyFlags, FBox& DirtyBounds)
{
const FOctreeElementId* ElementId = GetObjectsNavOctreeId(NodeOwner);
if (ElementId != NULL)
{
if (NavOctree->IsValidElementId(*ElementId))
{
// mark area occupied by given actor as dirty
FNavigationOctreeElement& ElementData = NavOctree->GetElementById(*ElementId);
DirtyFlags = ElementData.Data->GetDirtyFlag();
DirtyBounds = ElementData.Bounds.GetBox();
return true;
}
}
return false;
}
void UNavigationSystemV1::UnregisterNavOctreeElement(UObject* ElementOwner, INavRelevantInterface* ElementInterface, int32 UpdateFlags)
{
#if WITH_EDITOR
if (IsNavigationUnregisterLocked())
{
return;
}
#endif
if (NavOctree.IsValid() == false || ElementOwner == NULL || ElementInterface == NULL)
{
return;
}
if (IsNavigationOctreeLocked())
{
UE_LOG(LogNavOctree, Log, TEXT("IGNORE(UnregisterNavOctreeElement) %s"), *GetPathNameSafe(ElementOwner));
return;
}
const FOctreeElementId* ElementId = GetObjectsNavOctreeId(*ElementOwner);
UE_LOG(LogNavOctree, Log, TEXT("UNREG %s %s"), *GetNameSafe(ElementOwner), ElementId ? TEXT("[exists]") : TEXT(""));
if (ElementId != NULL)
{
RemoveNavOctreeElementId(*ElementId, UpdateFlags);
RemoveObjectsNavOctreeId(*ElementOwner);
}
else
{
const bool bCanRemoveChildNode = (UpdateFlags & OctreeUpdate_ParentChain) == 0;
UObject* ParentNode = ElementInterface->GetNavigationParent();
if (ParentNode && bCanRemoveChildNode)
{
// if node has navigation parent (= doesn't exists in octree on its own)
// and it's not part of parent chain update
// remove it from map and force update on parent to rebuild octree element
OctreeChildNodesMap.RemoveSingle(ParentNode, FWeakObjectPtr(ElementOwner));
UpdateNavOctreeParentChain(ParentNode);
}
}
// mark pending update as invalid, it will be dirtied according to currently active settings
const bool bCanInvalidateQueue = (UpdateFlags & OctreeUpdate_Refresh) == 0;
if (bCanInvalidateQueue)
{
const FSetElementId RequestId = PendingOctreeUpdates.FindId(FNavigationDirtyElement(ElementOwner));
if (RequestId.IsValidId())
{
PendingOctreeUpdates[RequestId].bInvalidRequest = true;
}
}
}
void UNavigationSystemV1::RemoveNavOctreeElementId(const FOctreeElementId& ElementId, int32 UpdateFlags)
{
if (NavOctree->IsValidElementId(ElementId))
{
const FNavigationOctreeElement& ElementData = NavOctree->GetElementById(ElementId);
const int32 DirtyFlag = GetDirtyFlagHelper(UpdateFlags, ElementData.Data->GetDirtyFlag());
// mark area occupied by given actor as dirty
AddDirtyArea(ElementData.Bounds.GetBox(), DirtyFlag);
NavOctree->RemoveNode(ElementId);
}
}
const FNavigationRelevantData* UNavigationSystemV1::GetDataForObject(const UObject& Object) const
{
check(NavOctree.IsValid());
const FOctreeElementId* OctreeID = GetObjectsNavOctreeId(Object);
if (OctreeID != nullptr && OctreeID->IsValidId() == true)
{
return NavOctree->GetDataForID(*OctreeID);
}
return nullptr;
}
void UNavigationSystemV1::UpdateActorInNavOctree(AActor& Actor)
{
if (IsNavigationSystemStatic())
{
return;
}
SCOPE_CYCLE_COUNTER(STAT_DebugNavOctree);
INavRelevantInterface* NavElement = Cast<INavRelevantInterface>(&Actor);
if (NavElement)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(Actor.GetWorld());
if (NavSys)
{
NavSys->UpdateNavOctreeElement(&Actor, NavElement, OctreeUpdate_Default);
}
}
}
void UNavigationSystemV1::UpdateComponentInNavOctree(UActorComponent& Comp)
{
SCOPE_CYCLE_COUNTER(STAT_DebugNavOctree);
if (ShouldUpdateNavOctreeOnComponentChange() == false)
{
return;
}
// special case for early out: use cached nav relevancy
if (Comp.bNavigationRelevant == true)
{
INavRelevantInterface* NavElement = Cast<INavRelevantInterface>(&Comp);
if (NavElement)
{
AActor* OwnerActor = Comp.GetOwner();
if (OwnerActor)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(OwnerActor->GetWorld());
if (NavSys)
{
if (OwnerActor->IsComponentRelevantForNavigation(&Comp))
{
NavSys->UpdateNavOctreeElement(&Comp, NavElement, OctreeUpdate_Default);
}
else
{
NavSys->UnregisterNavOctreeElement(&Comp, NavElement, OctreeUpdate_Default);
}
}
}
}
}
else if (Comp.CanEverAffectNavigation())
{
// could have been relevant before and not it isn't. Need to check if there's an octree element ID for it
INavRelevantInterface* NavElement = Cast<INavRelevantInterface>(&Comp);
if (NavElement)
{
AActor* OwnerActor = Comp.GetOwner();
if (OwnerActor)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(OwnerActor->GetWorld());
if (NavSys)
{
NavSys->UnregisterNavOctreeElement(&Comp, NavElement, OctreeUpdate_Default);
}
}
}
}
}
void UNavigationSystemV1::UpdateActorAndComponentsInNavOctree(AActor& Actor, bool bUpdateAttachedActors)
{
UpdateActorInNavOctree(Actor);
for (UActorComponent* Component : Actor.GetComponents())
{
if (Component)
{
UpdateComponentInNavOctree(*Component);
}
}
if (bUpdateAttachedActors)
{
UpdateAttachedActorsInNavOctree(Actor);
}
}
void UNavigationSystemV1::UpdateNavOctreeAfterMove(USceneComponent* Comp)
{
AActor* OwnerActor = Comp->GetOwner();
if (OwnerActor && OwnerActor->GetRootComponent() == Comp)
{
UpdateActorAndComponentsInNavOctree(*OwnerActor, true);
}
}
void UNavigationSystemV1::UpdateAttachedActorsInNavOctree(AActor& RootActor)
{
TArray<AActor*> UniqueAttachedActors;
UniqueAttachedActors.Add(&RootActor);
TArray<AActor*> TempAttachedActors;
for (int32 ActorIndex = 0; ActorIndex < UniqueAttachedActors.Num(); ++ActorIndex)
{
check(UniqueAttachedActors[ActorIndex]);
// find all attached actors
UniqueAttachedActors[ActorIndex]->GetAttachedActors(TempAttachedActors);
for (int32 AttachmentIndex = 0; AttachmentIndex < TempAttachedActors.Num(); ++AttachmentIndex)
{
// and store the ones we don't know about yet
UniqueAttachedActors.AddUnique(TempAttachedActors[AttachmentIndex]);
}
}
// skipping the first item since that's the root, and we just care about the attached actors
for (int32 ActorIndex = 1; ActorIndex < UniqueAttachedActors.Num(); ++ActorIndex)
{
UpdateActorAndComponentsInNavOctree(*UniqueAttachedActors[ActorIndex], /*bUpdateAttachedActors = */false);
}
}
void UNavigationSystemV1::UpdateNavOctreeBounds(AActor* Actor)
{
for (UActorComponent* Component : Actor->GetComponents())
{
INavRelevantInterface* NavElement = Cast<INavRelevantInterface>(Component);
if (NavElement)
{
NavElement->UpdateNavigationBounds();
}
}
}
void UNavigationSystemV1::ClearNavOctreeAll(AActor* Actor)
{
if (Actor)
{
OnActorUnregistered(Actor);
TInlineComponentArray<UActorComponent*> Components;
Actor->GetComponents(Components);
for (int32 Idx = 0; Idx < Components.Num(); Idx++)
{
OnComponentUnregistered(Components[Idx]);
}
}
}
void UNavigationSystemV1::UpdateNavOctreeElement(UObject* ElementOwner, INavRelevantInterface* ElementInterface, int32 UpdateFlags)
{
INC_DWORD_STAT(STAT_Navigation_UpdateNavOctree);
if (IsNavigationOctreeLocked())
{
UE_LOG(LogNavOctree, Log, TEXT("IGNORE(UpdateNavOctreeElement) %s"), *GetPathNameSafe(ElementOwner));
return;
}
else if (ElementOwner == nullptr)
{
return;
}
// grab existing octree data
FBox CurrentBounds;
int32 CurrentFlags;
const bool bAlreadyExists = GetNavOctreeElementData(*ElementOwner, CurrentFlags, CurrentBounds);
// don't invalidate pending requests
UpdateFlags |= OctreeUpdate_Refresh;
// always try to unregister, even if element owner doesn't exists in octree (parent nodes)
UnregisterNavOctreeElement(ElementOwner, ElementInterface, UpdateFlags);
const FSetElementId RequestId = RegisterNavOctreeElement(ElementOwner, ElementInterface, UpdateFlags);
// add original data to pending registration request
// so it could be dirtied properly when system receive unregister request while actor is still queued
if (RequestId.IsValidId())
{
FNavigationDirtyElement& UpdateInfo = PendingOctreeUpdates[RequestId];
UpdateInfo.PrevFlags = CurrentFlags;
if (UpdateInfo.PrevBounds.IsValid)
{
// Is we have something stored already we want to
// sum it up, since we care about the whole bounding
// box of changes that potentially took place
UpdateInfo.PrevBounds += CurrentBounds;
}
else
{
UpdateInfo.PrevBounds = CurrentBounds;
}
UpdateInfo.bHasPrevData = bAlreadyExists;
}
UpdateNavOctreeParentChain(ElementOwner, /*bSkipElementOwnerUpdate=*/ true);
}
void UNavigationSystemV1::UpdateNavOctreeParentChain(UObject* ElementOwner, bool bSkipElementOwnerUpdate)
{
const int32 UpdateFlags = OctreeUpdate_ParentChain | OctreeUpdate_Refresh;
TArray<FWeakObjectPtr> ChildNodes;
OctreeChildNodesMap.MultiFind(ElementOwner, ChildNodes);
if (ChildNodes.Num() == 0)
{
if (bSkipElementOwnerUpdate == false)
{
INavRelevantInterface* ElementInterface = Cast<INavRelevantInterface>(ElementOwner);
UpdateNavOctreeElement(ElementOwner, ElementInterface, UpdateFlags);
}
return;
}
INavRelevantInterface* ElementInterface = Cast<INavRelevantInterface>(ElementOwner);
TArray<INavRelevantInterface*> ChildNavInterfaces;
ChildNavInterfaces.AddZeroed(ChildNodes.Num());
for (int32 Idx = 0; Idx < ChildNodes.Num(); Idx++)
{
if (ChildNodes[Idx].IsValid())
{
UObject* ChildNodeOb = ChildNodes[Idx].Get();
ChildNavInterfaces[Idx] = Cast<INavRelevantInterface>(ChildNodeOb);
UnregisterNavOctreeElement(ChildNodeOb, ChildNavInterfaces[Idx], UpdateFlags);
}
}
if (bSkipElementOwnerUpdate == false)
{
UnregisterNavOctreeElement(ElementOwner, ElementInterface, UpdateFlags);
RegisterNavOctreeElement(ElementOwner, ElementInterface, UpdateFlags);
}
for (int32 Idx = 0; Idx < ChildNodes.Num(); Idx++)
{
if (ChildNodes[Idx].IsValid())
{
RegisterNavOctreeElement(ChildNodes[Idx].Get(), ChildNavInterfaces[Idx], UpdateFlags);
}
}
}
bool UNavigationSystemV1::UpdateNavOctreeElementBounds(UActorComponent* Comp, const FBox& NewBounds, const FBox& DirtyArea)
{
if (Comp == nullptr)
{
return false;
}
const FOctreeElementId* ElementId = GetObjectsNavOctreeId(*Comp);
if (ElementId && ElementId->IsValidId())
{
NavOctree->UpdateNode(*ElementId, NewBounds);
// Add dirty area
if (DirtyArea.IsValid)
{
ElementId = GetObjectsNavOctreeId(*Comp);
if (ElementId && ElementId->IsValidId())
{
FNavigationOctreeElement& ElementData = NavOctree->GetElementById(*ElementId);
AddDirtyArea(DirtyArea, ElementData.Data->GetDirtyFlag());
}
}
return true;
}
return false;
}
void UNavigationSystemV1::OnComponentRegistered(UActorComponent* Comp)
{
if (IsNavigationSystemStatic())
{
return;
}
SCOPE_CYCLE_COUNTER(STAT_DebugNavOctree);
INavRelevantInterface* NavInterface = Cast<INavRelevantInterface>(Comp);
if (NavInterface)
{
AActor* OwnerActor = Comp ? Comp->GetOwner() : NULL;
if (OwnerActor && OwnerActor->IsComponentRelevantForNavigation(Comp))
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(OwnerActor->GetWorld());
if (NavSys)
{
NavSys->RegisterNavOctreeElement(Comp, NavInterface, OctreeUpdate_Default);
}
}
}
}
void UNavigationSystemV1::OnComponentUnregistered(UActorComponent* Comp)
{
if (IsNavigationSystemStatic())
{
return;
}
SCOPE_CYCLE_COUNTER(STAT_DebugNavOctree);
INavRelevantInterface* NavInterface = Cast<INavRelevantInterface>(Comp);
if (NavInterface)
{
AActor* OwnerActor = Comp ? Comp->GetOwner() : NULL;
if (OwnerActor)
{
// skip IsComponentRelevantForNavigation check, it's only for adding new stuff
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(OwnerActor->GetWorld());
if (NavSys)
{
NavSys->UnregisterNavOctreeElement(Comp, NavInterface, OctreeUpdate_Default);
}
}
}
}
void UNavigationSystemV1::OnActorRegistered(AActor* Actor)
{
if (IsNavigationSystemStatic())
{
return;
}
SCOPE_CYCLE_COUNTER(STAT_DebugNavOctree);
INavRelevantInterface* NavInterface = Cast<INavRelevantInterface>(Actor);
if (NavInterface)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(Actor->GetWorld());
if (NavSys)
{
NavSys->RegisterNavOctreeElement(Actor, NavInterface, OctreeUpdate_Default);
}
}
}
void UNavigationSystemV1::OnActorUnregistered(AActor* Actor)
{
if (IsNavigationSystemStatic())
{
return;
}
SCOPE_CYCLE_COUNTER(STAT_DebugNavOctree);
INavRelevantInterface* NavInterface = Cast<INavRelevantInterface>(Actor);
if (NavInterface)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(Actor->GetWorld());
if (NavSys)
{
NavSys->UnregisterNavOctreeElement(Actor, NavInterface, OctreeUpdate_Default);
}
}
}
void UNavigationSystemV1::FindElementsInNavOctree(const FBox& QueryBox, const FNavigationOctreeFilter& Filter, TArray<FNavigationOctreeElement>& Elements)
{
if (NavOctree.IsValid() == false)
{
UE_LOG(LogNavigation, Warning, TEXT("UNavigationSystemV1::FindElementsInNavOctree gets called while NavOctree is null"));
return;
}
for (FNavigationOctree::TConstElementBoxIterator<> It(*NavOctree, QueryBox); It.HasPendingElements(); It.Advance())
{
const FNavigationOctreeElement& Element = It.GetCurrentElement();
if (Element.IsMatchingFilter(Filter))
{
Elements.Add(Element);
}
}
}
void UNavigationSystemV1::ReleaseInitialBuildingLock()
{
RemoveNavigationBuildLock(ENavigationBuildLock::InitialLock);
}
void UNavigationSystemV1::InitializeLevelCollisions()
{
if (IsNavigationSystemStatic())
{
bInitialLevelsAdded = true;
return;
}
UWorld* World = GetWorld();
if (!bInitialLevelsAdded && FNavigationSystem::GetCurrent<UNavigationSystemV1>(World) == this)
{
// Process all visible levels
const auto& Levels = World->GetLevels();
for (ULevel* Level : Levels)
{
if (Level->bIsVisible)
{
AddLevelCollisionToOctree(Level);
}
}
bInitialLevelsAdded = true;
}
}
#if WITH_EDITOR
void UNavigationSystemV1::UpdateLevelCollision(ULevel* InLevel)
{
if (InLevel != NULL)
{
UWorld* World = GetWorld();
OnLevelRemovedFromWorld(InLevel, World);
OnLevelAddedToWorld(InLevel, World);
}
}
void UNavigationSystemV1::OnEditorModeChanged(FEdMode* Mode, bool IsEntering)
{
if (Mode == NULL)
{
return;
}
if (IsEntering == false && Mode->GetID() == FBuiltinEditorModes::EM_Geometry)
{
// check if any of modified brushes belongs to an ANavMeshBoundsVolume
FEdModeGeometry* GeometryMode = (FEdModeGeometry*)Mode;
for (auto GeomObjectIt = GeometryMode->GeomObjectItor(); GeomObjectIt; GeomObjectIt++)
{
ANavMeshBoundsVolume* Volume = Cast<ANavMeshBoundsVolume>((*GeomObjectIt)->GetActualBrush());
if (Volume)
{
OnNavigationBoundsUpdated(Volume);
}
}
}
}
#endif
void UNavigationSystemV1::OnNavigationBoundsUpdated(ANavMeshBoundsVolume* NavVolume)
{
if (NavVolume == nullptr || IsNavigationSystemStatic())
{
return;
}
FNavigationBoundsUpdateRequest UpdateRequest;
UpdateRequest.NavBounds.UniqueID = NavVolume->GetUniqueID();
UpdateRequest.NavBounds.AreaBox = NavVolume->GetComponentsBoundingBox(true);
UpdateRequest.NavBounds.Level = NavVolume->GetLevel();
UpdateRequest.NavBounds.SupportedAgents = NavVolume->SupportedAgents;
UpdateRequest.UpdateRequest = FNavigationBoundsUpdateRequest::Updated;
AddNavigationBoundsUpdateRequest(UpdateRequest);
}
void UNavigationSystemV1::OnNavigationBoundsAdded(ANavMeshBoundsVolume* NavVolume)
{
if (NavVolume == nullptr || IsNavigationSystemStatic())
{
return;
}
FNavigationBoundsUpdateRequest UpdateRequest;
UpdateRequest.NavBounds.UniqueID = NavVolume->GetUniqueID();
UpdateRequest.NavBounds.AreaBox = NavVolume->GetComponentsBoundingBox(true);
UpdateRequest.NavBounds.Level = NavVolume->GetLevel();
UpdateRequest.NavBounds.SupportedAgents = NavVolume->SupportedAgents;
UpdateRequest.UpdateRequest = FNavigationBoundsUpdateRequest::Added;
AddNavigationBoundsUpdateRequest(UpdateRequest);
}
void UNavigationSystemV1::OnNavigationBoundsRemoved(ANavMeshBoundsVolume* NavVolume)
{
if (NavVolume == nullptr || IsNavigationSystemStatic())
{
return;
}
FNavigationBoundsUpdateRequest UpdateRequest;
UpdateRequest.NavBounds.UniqueID = NavVolume->GetUniqueID();
UpdateRequest.NavBounds.AreaBox = NavVolume->GetComponentsBoundingBox(true);
UpdateRequest.NavBounds.Level = NavVolume->GetLevel();
UpdateRequest.NavBounds.SupportedAgents = NavVolume->SupportedAgents;
UpdateRequest.UpdateRequest = FNavigationBoundsUpdateRequest::Removed;
AddNavigationBoundsUpdateRequest(UpdateRequest);
}
void UNavigationSystemV1::AddNavigationBoundsUpdateRequest(const FNavigationBoundsUpdateRequest& UpdateRequest)
{
int32 ExistingIdx = PendingNavBoundsUpdates.IndexOfByPredicate([&](const FNavigationBoundsUpdateRequest& Element) {
return UpdateRequest.NavBounds.UniqueID == Element.NavBounds.UniqueID;
});
if (ExistingIdx != INDEX_NONE)
{
// catch the case where the bounds was removed and immediately re-added with the same bounds as before
// in that case, we can cancel any update at all
bool bCanCancelUpdate = false;
if (PendingNavBoundsUpdates[ExistingIdx].UpdateRequest == FNavigationBoundsUpdateRequest::Removed && UpdateRequest.UpdateRequest == FNavigationBoundsUpdateRequest::Added)
{
for (TSet<FNavigationBounds>::TConstIterator It(RegisteredNavBounds); It; ++It)
{
if (*It == UpdateRequest.NavBounds)
{
bCanCancelUpdate = true;
break;
}
}
}
if (bCanCancelUpdate)
{
PendingNavBoundsUpdates.RemoveAt(ExistingIdx);
}
else
{
// Overwrite any previous updates
PendingNavBoundsUpdates[ExistingIdx] = UpdateRequest;
}
}
else
{
PendingNavBoundsUpdates.Add(UpdateRequest);
}
}
void UNavigationSystemV1::PerformNavigationBoundsUpdate(const TArray<FNavigationBoundsUpdateRequest>& UpdateRequests)
{
// NOTE: we used to create missing nav data first, before updating nav bounds,
// but some nav data classes (like RecastNavMesh) may depend on the nav bounds
// being already known at the moment of creation or serialization, so it makes more
// sense to update bounds first
// Create list of areas that needs to be updated
TArray<FBox> UpdatedAreas;
for (const FNavigationBoundsUpdateRequest& Request : UpdateRequests)
{
FSetElementId ExistingElementId = RegisteredNavBounds.FindId(Request.NavBounds);
switch (Request.UpdateRequest)
{
case FNavigationBoundsUpdateRequest::Removed:
{
if (ExistingElementId.IsValidId())
{
UpdatedAreas.Add(RegisteredNavBounds[ExistingElementId].AreaBox);
RegisteredNavBounds.Remove(ExistingElementId);
}
}
break;
case FNavigationBoundsUpdateRequest::Added:
case FNavigationBoundsUpdateRequest::Updated:
{
if (ExistingElementId.IsValidId())
{
const FBox ExistingBox = RegisteredNavBounds[ExistingElementId].AreaBox;
const bool bSameArea = (Request.NavBounds.AreaBox == ExistingBox);
if (!bSameArea)
{
UpdatedAreas.Add(ExistingBox);
}
// always assign new bounds data, it may have different properties (like supported agents)
RegisteredNavBounds[ExistingElementId] = Request.NavBounds;
}
else
{
AddNavigationBounds(Request.NavBounds);
}
UpdatedAreas.Add(Request.NavBounds.AreaBox);
}
break;
}
}
if (!IsNavigationBuildingLocked())
{
if (UpdatedAreas.Num())
{
for (ANavigationData* NavData : NavDataSet)
{
if (NavData)
{
NavData->OnNavigationBoundsChanged();
}
}
}
// Propagate to generators areas that needs to be updated
AddDirtyAreas(UpdatedAreas, ENavigationDirtyFlag::All | ENavigationDirtyFlag::NavigationBounds);
}
// I'm not sure why we even do the following as part of this function
// @TODO investigate if we can extract it into a separate function and
// call it directly
if (NavDataSet.Num() == 0)
{
//TODO: will hitch when user places first navigation volume in the world
if (NavDataRegistrationQueue.Num() > 0)
{
ProcessRegistrationCandidates();
}
if (NavDataSet.Num() == 0 && bAutoCreateNavigationData == true)
{
SpawnMissingNavigationData();
ProcessRegistrationCandidates();
}
ConditionalPopulateNavOctree();
}
}
void UNavigationSystemV1::AddNavigationBounds(const FNavigationBounds& NewBounds)
{
RegisteredNavBounds.Add(NewBounds);
}
void UNavigationSystemV1::GatherNavigationBounds()
{
// Gather all available navigation bounds
RegisteredNavBounds.Empty();
for (TActorIterator<ANavMeshBoundsVolume> It(GetWorld()); It; ++It)
{
ANavMeshBoundsVolume* V = (*It);
if (V != nullptr && !V->IsPendingKill())
{
FNavigationBounds NavBounds;
NavBounds.UniqueID = V->GetUniqueID();
NavBounds.AreaBox = V->GetComponentsBoundingBox(true);
NavBounds.Level = V->GetLevel();
NavBounds.SupportedAgents = V->SupportedAgents;
AddNavigationBounds(NavBounds);
}
}
}
void UNavigationSystemV1::Build()
{
UWorld* World = GetWorld();
if (!World)
{
UE_LOG(LogNavigation, Error, TEXT("Unable to build navigation due to missing World pointer"));
return;
}
FNavigationSystem::DiscardNavigationDataChunks(*World);
const bool bHasWork = IsThereAnywhereToBuildNavigation();
const bool bLockedIgnoreEditor = (NavBuildingLockFlags & ~ENavigationBuildLock::NoUpdateInEditor) != 0;
if (!bHasWork || bLockedIgnoreEditor)
{
return;
}
const double BuildStartTime = FPlatformTime::Seconds();
if (bAutoCreateNavigationData == true
#if WITH_EDITOR
|| OperationMode == FNavigationSystemRunMode::EditorMode
#endif // WITH_EDITOR
)
{
SpawnMissingNavigationData();
}
// make sure freshly created navigation instances are registered before we try to build them
ProcessRegistrationCandidates();
// and now iterate through all registered and just start building them
RebuildAll();
// Block until build is finished
for (ANavigationData* NavData : NavDataSet)
{
if (NavData)
{
NavData->EnsureBuildCompletion();
}
}
#if !UE_BUILD_SHIPPING
// no longer report that navmesh needs to be rebuild
bDirtyAreasReportedWhileAccumulationLocked = false;
#endif // !UE_BUILD_SHIPPING
UE_LOG(LogNavigation, Display, TEXT("UNavigationSystemV1::Build total execution time: %.5f"), float(FPlatformTime::Seconds() - BuildStartTime));
}
void UNavigationSystemV1::CancelBuild()
{
for (ANavigationData* NavData : NavDataSet)
{
if (NavData)
{
if (NavData->GetGenerator())
{
NavData->GetGenerator()->CancelBuild();
}
}
}
}
void UNavigationSystemV1::SpawnMissingNavigationData()
{
const int32 SupportedAgentsCount = SupportedAgents.Num();
check(SupportedAgentsCount >= 0);
// Bit array might be a bit of an overkill here, but this function will be called very rarely
TBitArray<> AlreadyInstantiated(false, SupportedAgentsCount);
uint8 NumberFound = 0;
UWorld* NavWorld = GetWorld();
// 1. check whether any of required navigation data has already been instantiated
for (TActorIterator<ANavigationData> It(NavWorld); It && NumberFound < SupportedAgentsCount; ++It)
{
ANavigationData* Nav = (*It);
if (Nav != nullptr
&& Nav->IsPendingKill() == false
// mz@todo the 'is level in' condition is temporary
&& (Nav->GetTypedOuter<UWorld>() == NavWorld || NavWorld->GetLevels().Contains(Nav->GetLevel())))
{
// find out which one it is
for (int32 AgentIndex = 0; AgentIndex < SupportedAgentsCount; ++AgentIndex)
{
if (AlreadyInstantiated[AgentIndex] == false
&& Nav->GetClass() == SupportedAgents[AgentIndex].NavigationDataClass
&& Nav->DoesSupportAgent(SupportedAgents[AgentIndex]) == true)
{
AlreadyInstantiated[AgentIndex] = true;
++NumberFound;
break;
}
}
}
}
// 2. for any not already instantiated navigation data call creator functions
if (NumberFound < SupportedAgentsCount)
{
for (int32 AgentIndex = 0; AgentIndex < SupportedAgentsCount; ++AgentIndex)
{
const FNavDataConfig& NavConfig = SupportedAgents[AgentIndex];
if (AlreadyInstantiated[AgentIndex] == false && NavConfig.NavigationDataClass != nullptr)
{
bool bHandled = false;
const ANavigationData* NavDataCDO = NavConfig.NavigationDataClass->GetDefaultObject<ANavigationData>();
if (NavDataCDO == nullptr || !NavDataCDO->CanSpawnOnRebuild())
{
continue;
}
if (NavWorld->WorldType != EWorldType::Editor && NavDataCDO->GetRuntimeGenerationMode() == ERuntimeGenerationType::Static)
{
// if we're not in the editor, and specified navigation class is configured
// to be static, then we don't want to create an instance
UE_LOG(LogNavigation, Log, TEXT("Not spawning navigation data for %s since indivated NavigationData type is not configured for dynamic generation")
, *NavConfig.Name.ToString());
continue;
}
ANavigationData* Instance = CreateNavigationDataInstance(NavConfig);
if (Instance)
{
RequestRegistration(Instance);
}
else
{
UE_LOG(LogNavigation, Warning, TEXT("Was not able to create navigation data for SupportedAgent[%d]: %s"), AgentIndex, *NavConfig.Name.ToString());
}
}
}
}
if (MainNavData == nullptr || MainNavData->IsPendingKillPending())
{
// update
MainNavData = GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
}
}
ANavigationData* UNavigationSystemV1::CreateNavigationDataInstance(const FNavDataConfig& NavConfig)
{
UWorld* World = GetWorld();
check(World);
FActorSpawnParameters SpawnInfo;
if (bSpawnNavDataInNavBoundsLevel && RegisteredNavBounds.Num() > 0)
{
// pick the first valid level
for (const FNavigationBounds& Bounds : RegisteredNavBounds)
{
if (Bounds.Level.IsValid())
{
SpawnInfo.OverrideLevel = Bounds.Level.Get();
break;
}
}
}
if (SpawnInfo.OverrideLevel == nullptr)
{
SpawnInfo.OverrideLevel = World->PersistentLevel;
}
ANavigationData* Instance = World->SpawnActor<ANavigationData>(*NavConfig.NavigationDataClass, SpawnInfo);
if (Instance != NULL)
{
Instance->SetConfig(NavConfig);
if (NavConfig.Name != NAME_None)
{
FString StrName = FString::Printf(TEXT("%s-%s"), *(Instance->GetFName().GetPlainNameString()), *(NavConfig.Name.ToString()));
// temporary solution to make sure we don't try to change name while there's already
// an object with this name
UObject* ExistingObject = StaticFindObject(/*Class=*/ NULL, Instance->GetOuter(), *StrName, true);
if (ExistingObject != NULL)
{
ANavigationData* ExistingNavigationData = Cast<ANavigationData>(ExistingObject);
if (ExistingNavigationData)
{
UnregisterNavData(ExistingNavigationData);
AgentToNavDataMap.Remove(ExistingNavigationData->GetConfig());
}
ExistingObject->Rename(NULL, NULL, REN_DontCreateRedirectors | REN_ForceGlobalUnique | REN_DoNotDirty | REN_NonTransactional | REN_ForceNoResetLoaders);
}
// Set descriptive name
Instance->Rename(*StrName, NULL, REN_DoNotDirty | REN_ForceNoResetLoaders);
#if WITH_EDITOR
if (World->WorldType == EWorldType::Editor)
{
const bool bMarkDirty = false;
Instance->SetActorLabel(StrName, bMarkDirty);
}
#endif // WITH_EDITOR
}
}
return Instance;
}
void UNavigationSystemV1::OnPIEStart()
{
bIsPIEActive = true;
// no updates for editor world while PIE is active
const UWorld* MyWorld = GetWorld();
if (MyWorld && !MyWorld->IsGameWorld())
{
bAsyncBuildPaused = true;
AddNavigationBuildLock(ENavigationBuildLock::NoUpdateInEditor);
}
}
void UNavigationSystemV1::OnPIEEnd()
{
bIsPIEActive = false;
const UWorld* MyWorld = GetWorld();
if (MyWorld && !MyWorld->IsGameWorld())
{
bAsyncBuildPaused = false;
// there's no need to request while navigation rebuilding just because PIE has ended
RemoveNavigationBuildLock(ENavigationBuildLock::NoUpdateInEditor, /*bSkipRebuildInEditor=*/true);
}
}
void UNavigationSystemV1::RemoveNavigationBuildLock(uint8 Flags, bool bSkipRebuildInEditor)
{
const bool bWasLocked = IsNavigationBuildingLocked();
NavBuildingLockFlags &= ~Flags;
const bool bIsLocked = IsNavigationBuildingLocked();
const bool bSkipRebuild = (OperationMode == FNavigationSystemRunMode::EditorMode) && bSkipRebuildInEditor;
if (bWasLocked && !bIsLocked && !bSkipRebuild)
{
RebuildAll();
}
}
void UNavigationSystemV1::RebuildAll(bool bIsLoadTime)
{
const bool bIsInGame = GetWorld()->IsGameWorld();
GatherNavigationBounds();
// make sure that octree is up to date
for (TSet<FNavigationDirtyElement>::TIterator It(PendingOctreeUpdates); It; ++It)
{
AddElementToNavOctree(*It);
}
PendingOctreeUpdates.Empty(32);
// discard all pending dirty areas, we are going to rebuild navmesh anyway
DirtyAreas.Reset();
PendingNavBoundsUpdates.Reset();
#if !UE_BUILD_SHIPPING
bDirtyAreasReportedWhileAccumulationLocked = false;
#endif // !UE_BUILD_SHIPPING
//
for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex)
{
ANavigationData* NavData = NavDataSet[NavDataIndex];
if (NavData && (!bIsLoadTime || NavData->NeedsRebuildOnLoad()) && (!bIsInGame || NavData->SupportsRuntimeGeneration()))
{
NavData->RebuildAll();
}
}
}
bool UNavigationSystemV1::IsNavigationBuildInProgress(bool bCheckDirtyToo)
{
bool bRet = false;
if (NavDataSet.Num() == 0)
{
// @todo this is wrong! Should not need to create a navigation data instance in a "getter" like function
// update nav data. If none found this is the place to create one
GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
}
for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex)
{
ANavigationData* NavData = NavDataSet[NavDataIndex];
if (NavData != NULL && NavData->GetGenerator() != NULL
&& NavData->GetGenerator()->IsBuildInProgress(bCheckDirtyToo) == true)
{
bRet = true;
break;
}
}
return bRet;
}
void UNavigationSystemV1::OnNavigationGenerationFinished(ANavigationData& NavData)
{
OnNavigationGenerationFinishedDelegate.Broadcast(&NavData);
}
int32 UNavigationSystemV1::GetNumRemainingBuildTasks() const
{
int32 NumTasks = 0;
for (ANavigationData* NavData : NavDataSet)
{
if (NavData && NavData->GetGenerator())
{
NumTasks+= NavData->GetGenerator()->GetNumRemaningBuildTasks();
}
}
return NumTasks;
}
int32 UNavigationSystemV1::GetNumRunningBuildTasks() const
{
int32 NumTasks = 0;
for (ANavigationData* NavData : NavDataSet)
{
if (NavData && NavData->GetGenerator())
{
NumTasks+= NavData->GetGenerator()->GetNumRunningBuildTasks();
}
}
return NumTasks;
}
void UNavigationSystemV1::OnLevelAddedToWorld(ULevel* InLevel, UWorld* InWorld)
{
if ((IsNavigationSystemStatic() == false) && (InWorld == GetWorld()))
{
AddLevelCollisionToOctree(InLevel);
if (!InLevel->IsPersistentLevel())
{
for (ANavigationData* NavData : NavDataSet)
{
if (NavData)
{
NavData->OnStreamingLevelAdded(InLevel, InWorld);
}
}
}
}
}
void UNavigationSystemV1::OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld)
{
if ((IsNavigationSystemStatic() == false) && (InWorld == GetWorld()))
{
RemoveLevelCollisionFromOctree(InLevel);
if (InLevel && !InLevel->IsPersistentLevel())
{
for (int32 DataIndex = NavDataSet.Num() - 1; DataIndex >= 0; --DataIndex)
{
ANavigationData* NavData = NavDataSet[DataIndex];
if (NavData)
{
if (NavData->GetLevel() != InLevel)
{
NavData->OnStreamingLevelRemoved(InLevel, InWorld);
}
else
{
NavDataSet.RemoveAt(DataIndex, 1, /*bAllowShrinking=*/false);
}
}
}
}
}
}
void UNavigationSystemV1::AddLevelCollisionToOctree(ULevel* Level)
{
#if WITH_RECAST
if (Level && NavOctree.IsValid() &&
NavOctree->GetNavGeometryStoringMode() == FNavigationOctree::StoreNavGeometry)
{
const TArray<FVector>* LevelGeom = Level->GetStaticNavigableGeometry();
const FOctreeElementId* ElementId = GetObjectsNavOctreeId(*Level);
if (!ElementId && LevelGeom && LevelGeom->Num() > 0)
{
FNavigationOctreeElement BSPElem(*Level);
FRecastNavMeshGenerator::ExportVertexSoupGeometry(*LevelGeom, *BSPElem.Data);
const auto& Bounds = BSPElem.Data->Bounds;
if (!Bounds.GetExtent().IsNearlyZero())
{
NavOctree->AddNode(Level, NULL, Bounds, BSPElem);
AddDirtyArea(Bounds, ENavigationDirtyFlag::All);
UE_LOG(LogNavOctree, Log, TEXT("ADD %s"), *GetNameSafe(Level));
}
}
}
#endif// WITH_RECAST
}
void UNavigationSystemV1::RemoveLevelCollisionFromOctree(ULevel* Level)
{
if (Level && NavOctree.IsValid())
{
const FOctreeElementId* ElementId = GetObjectsNavOctreeId(*Level);
UE_LOG(LogNavOctree, Log, TEXT("UNREG %s %s"), *GetNameSafe(Level), ElementId ? TEXT("[exists]") : TEXT(""));
if (ElementId != NULL)
{
if (NavOctree->IsValidElementId(*ElementId))
{
// mark area occupied by given actor as dirty
FNavigationOctreeElement& ElementData = NavOctree->GetElementById(*ElementId);
AddDirtyArea(ElementData.Bounds.GetBox(), ENavigationDirtyFlag::All);
}
NavOctree->RemoveNode(*ElementId);
RemoveObjectsNavOctreeId(*Level);
}
}
}
void UNavigationSystemV1::OnPostLoadMap(UWorld*)
{
UE_LOG(LogNavigation, Log, TEXT("UNavigationSystemV1::OnPostLoadMap"));
// if map has been loaded and there are some navigation bounds volumes
// then create appropriate navigation structured
ANavigationData* NavData = GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
// Do this if there's currently no navigation
if (NavData == NULL && bAutoCreateNavigationData == true && IsThereAnywhereToBuildNavigation() == true)
{
NavData = GetDefaultNavDataInstance(FNavigationSystem::Create);
}
}
#if WITH_EDITOR
void UNavigationSystemV1::OnActorMoved(AActor* Actor)
{
if (Cast<ANavMeshBoundsVolume>(Actor) != NULL)
{
OnNavigationBoundsUpdated((ANavMeshBoundsVolume*)Actor);
}
}
#endif // WITH_EDITOR
void UNavigationSystemV1::OnNavigationDirtied(const FBox& Bounds)
{
AddDirtyArea(Bounds, ENavigationDirtyFlag::All);
}
#if WITH_HOT_RELOAD
void UNavigationSystemV1::OnHotReload(bool bWasTriggeredAutomatically)
{
if (RequiresNavOctree() && NavOctree.IsValid() == false)
{
ConditionalPopulateNavOctree();
if (bInitialBuildingLocked)
{
RemoveNavigationBuildLock(ENavigationBuildLock::InitialLock, /*bSkipRebuildInEditor=*/true);
}
}
}
#endif // WITH_HOT_RELOAD
void UNavigationSystemV1::CleanUp(FNavigationSystem::ECleanupMode Mode)
{
UE_LOG(LogNavigation, Log, TEXT("UNavigationSystemV1::CleanUp"));
#if WITH_EDITOR
if (GIsEditor && GEngine)
{
GEngine->OnActorMoved().RemoveAll(this);
}
#endif // WITH_EDITOR
FCoreUObjectDelegates::PostLoadMapWithWorld.RemoveAll(this);
UNavigationSystemV1::NavigationDirtyEvent.RemoveAll(this);
FWorldDelegates::LevelAddedToWorld.RemoveAll(this);
FWorldDelegates::LevelRemovedFromWorld.RemoveAll(this);
#if WITH_HOT_RELOAD
if (IHotReloadInterface* HotReloadSupport = FModuleManager::GetModulePtr<IHotReloadInterface>("HotReload"))
{
HotReloadSupport->OnHotReload().Remove(HotReloadDelegateHandle);
}
#endif
DestroyNavOctree();
SetCrowdManager(NULL);
NavDataSet.Reset();
// reset unique link Id for new map
const UWorld* MyWorld = (Mode == FNavigationSystem::ECleanupMode::CleanupWithWorld) ? GetWorld() : NULL;
if (MyWorld && (MyWorld->WorldType == EWorldType::Game || MyWorld->WorldType == EWorldType::Editor))
{
INavLinkCustomInterface::NextUniqueId = 1;
}
}
void UNavigationSystemV1::DestroyNavOctree()
{
if (NavOctree.IsValid())
{
NavOctree->Destroy();
NavOctree = NULL;
}
ObjectToOctreeId.Empty();
}
bool UNavigationSystemV1::RequiresNavOctree() const
{
UWorld* World = GetWorld();
check(World);
// We always require navoctree in editor worlds
if (!World->IsGameWorld())
{
return true;
}
for (ANavigationData* NavData : NavDataSet)
{
if (NavData && NavData->SupportsRuntimeGeneration())
{
return true;
}
}
return false;
}
ERuntimeGenerationType UNavigationSystemV1::GetRuntimeGenerationType() const
{
UWorld* World = GetWorld();
check(World);
// We always use ERuntimeGenerationType::Dynamic in editor worlds
if (!World->IsGameWorld())
{
return ERuntimeGenerationType::Dynamic;
}
ERuntimeGenerationType RuntimeGenerationType = ERuntimeGenerationType::Static;
for (ANavigationData* NavData : NavDataSet)
{
if (NavData && NavData->GetRuntimeGenerationMode() > RuntimeGenerationType)
{
RuntimeGenerationType = NavData->GetRuntimeGenerationMode();
}
}
return RuntimeGenerationType;
}
//----------------------------------------------------------------------//
// Blueprint functions
//----------------------------------------------------------------------//
UNavigationSystemV1* UNavigationSystemV1::GetNavigationSystem(UObject* WorldContextObject)
{
return GetCurrent(WorldContextObject);
}
bool UNavigationSystemV1::K2_ProjectPointToNavigation(UObject* WorldContextObject, const FVector& Point, FVector& ProjectedLocation, ANavigationData* NavData, TSubclassOf<UNavigationQueryFilter> FilterClass, const FVector QueryExtent)
{
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
ProjectedLocation = Point;
bool bResult = false;
if (NavSys)
{
FNavLocation OutNavLocation;
ANavigationData* UseNavData = NavData ? NavData : NavSys->GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
if (UseNavData)
{
bResult = NavSys->ProjectPointToNavigation(Point, OutNavLocation, QueryExtent, NavData
, UNavigationQueryFilter::GetQueryFilter(*UseNavData, WorldContextObject, FilterClass));
ProjectedLocation = OutNavLocation.Location;
}
}
return bResult;
}
bool UNavigationSystemV1::K2_GetRandomReachablePointInRadius(UObject* WorldContextObject, const FVector& Origin, FVector& RandomLocation, float Radius, ANavigationData* NavData, TSubclassOf<UNavigationQueryFilter> FilterClass)
{
FNavLocation RandomPoint(Origin);
bool bResult = false;
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys)
{
ANavigationData* UseNavData = NavData ? NavData : NavSys->GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
if (UseNavData)
{
bResult = NavSys->GetRandomReachablePointInRadius(Origin, Radius, RandomPoint, UseNavData, UNavigationQueryFilter::GetQueryFilter(*UseNavData, WorldContextObject, FilterClass));
RandomLocation = RandomPoint.Location;
}
}
return bResult;
}
bool UNavigationSystemV1::K2_GetRandomPointInNavigableRadius(UObject* WorldContextObject, const FVector& Origin, FVector& RandomLocation, float Radius, ANavigationData* NavData, TSubclassOf<UNavigationQueryFilter> FilterClass)
{
FNavLocation RandomPoint(Origin);
bool bResult = false;
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys)
{
ANavigationData* UseNavData = NavData ? NavData : NavSys->GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
if (UseNavData)
{
bResult = NavSys->GetRandomPointInNavigableRadius(Origin, Radius, RandomPoint, UseNavData, UNavigationQueryFilter::GetQueryFilter(*UseNavData, WorldContextObject, FilterClass));
RandomLocation = RandomPoint.Location;
}
}
return bResult;
}
ENavigationQueryResult::Type UNavigationSystemV1::GetPathCost(UObject* WorldContextObject, const FVector& PathStart, const FVector& PathEnd, float& OutPathCost, ANavigationData* NavData, TSubclassOf<UNavigationQueryFilter> FilterClass)
{
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys)
{
ANavigationData* UseNavData = NavData ? NavData : NavSys->GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
if (UseNavData)
{
return NavSys->GetPathCost(PathStart, PathEnd, OutPathCost, UseNavData, UNavigationQueryFilter::GetQueryFilter(*UseNavData, WorldContextObject, FilterClass));
}
}
return ENavigationQueryResult::Error;
}
ENavigationQueryResult::Type UNavigationSystemV1::GetPathLength(UObject* WorldContextObject, const FVector& PathStart, const FVector& PathEnd, float& OutPathLength, ANavigationData* NavData, TSubclassOf<UNavigationQueryFilter> FilterClass)
{
float PathLength = 0.f;
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys)
{
ANavigationData* UseNavData = NavData ? NavData : NavSys->GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
if (UseNavData)
{
return NavSys->GetPathLength(PathStart, PathEnd, OutPathLength, UseNavData, UNavigationQueryFilter::GetQueryFilter(*UseNavData, WorldContextObject, FilterClass));
}
}
return ENavigationQueryResult::Error;
}
bool UNavigationSystemV1::IsNavigationBeingBuilt(UObject* WorldContextObject)
{
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys && !NavSys->IsNavigationBuildingPermanentlyLocked())
{
return NavSys->HasDirtyAreasQueued() || NavSys->IsNavigationBuildInProgress();
}
return false;
}
bool UNavigationSystemV1::IsNavigationBeingBuiltOrLocked(UObject* WorldContextObject)
{
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys)
{
return NavSys->IsNavigationBuildingLocked() || NavSys->HasDirtyAreasQueued() || NavSys->IsNavigationBuildInProgress();
}
return false;
}
//----------------------------------------------------------------------//
// HACKS!!!
//----------------------------------------------------------------------//
bool UNavigationSystemV1::ShouldGeneratorRun(const FNavDataGenerator* Generator) const
{
if (Generator != NULL && (IsNavigationSystemStatic() == false))
{
for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex)
{
ANavigationData* NavData = NavDataSet[NavDataIndex];
if (NavData != NULL && NavData->GetGenerator() == Generator)
{
return true;
}
}
}
return false;
}
bool UNavigationSystemV1::HandleCycleNavDrawnCommand( const TCHAR* Cmd, FOutputDevice& Ar )
{
CycleNavigationDataDrawn();
return true;
}
bool UNavigationSystemV1::HandleCountNavMemCommand()
{
UE_LOG(LogNavigation, Warning, TEXT("Logging NavigationSystem memory usage:"));
if (NavOctree.IsValid())
{
UE_LOG(LogNavigation, Warning, TEXT("NavOctree memory: %d"), NavOctree->GetSizeBytes());
}
for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex)
{
ANavigationData* NavData = NavDataSet[NavDataIndex];
if (NavData != NULL)
{
NavData->LogMemUsed();
}
}
return true;
}
//----------------------------------------------------------------------//
// Commands
//----------------------------------------------------------------------//
bool FNavigationSystemExec::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(InWorld);
if (NavSys && NavSys->NavDataSet.Num() > 0)
{
if (FParse::Command(&Cmd, TEXT("CYCLENAVDRAWN")))
{
NavSys->HandleCycleNavDrawnCommand( Cmd, Ar );
// not returning true to enable all navigation systems to cycle their own data
return false;
}
else if (FParse::Command(&Cmd, TEXT("CountNavMem")))
{
NavSys->HandleCountNavMemCommand();
return false;
}
/** Builds the navigation mesh (or rebuilds it). **/
else if (FParse::Command(&Cmd, TEXT("RebuildNavigation")))
{
NavSys->Build();
}
}
return false;
}
void UNavigationSystemV1::CycleNavigationDataDrawn()
{
++CurrentlyDrawnNavDataIndex;
if (CurrentlyDrawnNavDataIndex >= NavDataSet.Num())
{
CurrentlyDrawnNavDataIndex = INDEX_NONE;
}
for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex)
{
ANavigationData* NavData = NavDataSet[NavDataIndex];
if (NavData != NULL)
{
const bool bNewEnabledDrawing = (CurrentlyDrawnNavDataIndex == INDEX_NONE) || (NavDataIndex == CurrentlyDrawnNavDataIndex);
NavData->SetNavRenderingEnabled(bNewEnabledDrawing);
}
}
}
bool UNavigationSystemV1::IsNavigationDirty() const
{
#if !UE_BUILD_SHIPPING
if (bCanAccumulateDirtyAreas == false && bDirtyAreasReportedWhileAccumulationLocked)
{
return true;
}
#endif // !UE_BUILD_SHIPPING
for (int32 NavDataIndex=0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex)
{
if (NavDataSet[NavDataIndex] && NavDataSet[NavDataIndex]->NeedsRebuild())
{
return true;
}
}
return false;
}
bool UNavigationSystemV1::CanRebuildDirtyNavigation() const
{
const bool bIsInGame = GetWorld()->IsGameWorld();
for (const ANavigationData* NavData : NavDataSet)
{
if (NavData)
{
const bool bIsDirty = NavData->NeedsRebuild();
const bool bCanRebuild = !bIsInGame || NavData->SupportsRuntimeGeneration();
if (bIsDirty && !bCanRebuild)
{
return false;
}
}
}
return true;
}
bool UNavigationSystemV1::DoesPathIntersectBox(const FNavigationPath* Path, const FBox& Box, uint32 StartingIndex, FVector* AgentExtent)
{
return Path != NULL && Path->DoesIntersectBox(Box, StartingIndex, NULL, AgentExtent);
}
bool UNavigationSystemV1::DoesPathIntersectBox(const FNavigationPath* Path, const FBox& Box, const FVector& AgentLocation, uint32 StartingIndex, FVector* AgentExtent)
{
return Path != NULL && Path->DoesIntersectBox(Box, AgentLocation, StartingIndex, NULL, AgentExtent);
}
void UNavigationSystemV1::SetMaxSimultaneousTileGenerationJobsCount(int32 MaxNumberOfJobs)
{
#if WITH_RECAST
for (auto NavigationData : NavDataSet)
{
ARecastNavMesh* RecastNavMesh = Cast<ARecastNavMesh>(NavigationData);
if (RecastNavMesh)
{
RecastNavMesh->SetMaxSimultaneousTileGenerationJobsCount(MaxNumberOfJobs);
}
}
#endif
}
void UNavigationSystemV1::ResetMaxSimultaneousTileGenerationJobsCount()
{
#if WITH_RECAST
for (auto NavigationData : NavDataSet)
{
ARecastNavMesh* RecastNavMesh = Cast<ARecastNavMesh>(NavigationData);
if (RecastNavMesh)
{
const ARecastNavMesh* CDO = RecastNavMesh->GetClass()->GetDefaultObject<ARecastNavMesh>();
RecastNavMesh->SetMaxSimultaneousTileGenerationJobsCount(CDO->MaxSimultaneousTileGenerationJobsCount);
}
}
#endif
}
//----------------------------------------------------------------------//
// Active tiles
//----------------------------------------------------------------------//
void UNavigationSystemV1::RegisterNavigationInvoker(AActor& Invoker, float TileGenerationRadius, float TileRemovalRadius)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(Invoker.GetWorld());
if (NavSys)
{
NavSys->RegisterInvoker(Invoker, TileGenerationRadius, TileRemovalRadius);
}
}
void UNavigationSystemV1::UnregisterNavigationInvoker(AActor& Invoker)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(Invoker.GetWorld());
if (NavSys)
{
NavSys->UnregisterInvoker(Invoker);
}
}
void UNavigationSystemV1::SetGeometryGatheringMode(ENavDataGatheringModeConfig NewMode)
{
DataGatheringMode = NewMode;
if (NavOctree.IsValid())
{
NavOctree->SetDataGatheringMode(DataGatheringMode);
}
}
void UNavigationSystemV1::RegisterInvoker(AActor& Invoker, float TileGenerationRadius, float TileRemovalRadius)
{
UE_CVLOG(bGenerateNavigationOnlyAroundNavigationInvokers == false, this, LogNavigation, Warning
, TEXT("Trying to register %s as enforcer, but NavigationSystem is not set up for enforcer-centric generation. See GenerateNavigationOnlyAroundNavigationInvokers in NavigationSystem's properties")
, *Invoker.GetName());
TileGenerationRadius = FMath::Clamp(TileGenerationRadius, 0.f, BIG_NUMBER);
TileRemovalRadius = FMath::Clamp(TileRemovalRadius, TileGenerationRadius, BIG_NUMBER);
FNavigationInvoker& Data = Invokers.FindOrAdd(&Invoker);
Data.Actor = &Invoker;
Data.GenerationRadius = TileGenerationRadius;
Data.RemovalRadius = TileRemovalRadius;
UE_VLOG_CYLINDER(this, LogNavigation, Log, Invoker.GetActorLocation(), Invoker.GetActorLocation() + FVector(0, 0, 20), TileGenerationRadius, FColorList::LimeGreen
, TEXT("%s %.0f %.0f"), *Invoker.GetName(), TileGenerationRadius, TileRemovalRadius);
UE_VLOG_CYLINDER(this, LogNavigation, Log, Invoker.GetActorLocation(), Invoker.GetActorLocation() + FVector(0, 0, 20), TileRemovalRadius, FColorList::IndianRed, TEXT(""));
}
void UNavigationSystemV1::UnregisterInvoker(AActor& Invoker)
{
UE_VLOG(this, LogNavigation, Log, TEXT("Removing %s from enforcers list"), *Invoker.GetName());
Invokers.Remove(&Invoker);
}
void UNavigationSystemV1::UpdateInvokers()
{
UWorld* World = GetWorld();
const float CurrentTime = World->GetTimeSeconds();
if (CurrentTime >= NextInvokersUpdateTime)
{
TArray<FNavigationInvokerRaw> InvokerLocations;
if (Invokers.Num() > 0)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_NavSys_Clusterize);
const double StartTime = FPlatformTime::Seconds();
InvokerLocations.Reserve(Invokers.Num());
for (auto ItemIterator = Invokers.CreateIterator(); ItemIterator; ++ItemIterator)
{
AActor* Actor = ItemIterator->Value.Actor.Get();
if (Actor != nullptr
#if WITH_EDITOR
// Would like to ignore objects in transactional buffer here, but there's no flag for it
//&& (GIsEditor == false || Item.Actor->HasAnyFlags(RF_Transactional | RF_PendingKill) == false)
#endif //WITH_EDITOR
)
{
InvokerLocations.Add(FNavigationInvokerRaw(Actor->GetActorLocation(), ItemIterator->Value.GenerationRadius, ItemIterator->Value.RemovalRadius));
}
else
{
ItemIterator.RemoveCurrent();
}
}
#if ENABLE_VISUAL_LOG
const double CachingFinishTime = FPlatformTime::Seconds();
UE_VLOG(this, LogNavigation, Log, TEXT("Caching time %fms"), (CachingFinishTime - StartTime) * 1000);
for (const auto& InvokerData : InvokerLocations)
{
UE_VLOG_CYLINDER(this, LogNavigation, Log, InvokerData.Location, InvokerData.Location + FVector(0, 0, 20), InvokerData.RadiusMax, FColorList::Blue, TEXT(""));
UE_VLOG_CYLINDER(this, LogNavigation, Log, InvokerData.Location, InvokerData.Location + FVector(0, 0, 20), InvokerData.RadiusMin, FColorList::CadetBlue, TEXT(""));
}
#endif // ENABLE_VISUAL_LOG
}
#if WITH_RECAST
const double UpdateStartTime = FPlatformTime::Seconds();
for (TActorIterator<ARecastNavMesh> It(GetWorld()); It; ++It)
{
It->UpdateActiveTiles(InvokerLocations);
}
const double UpdateEndTime = FPlatformTime::Seconds();
UE_VLOG(this, LogNavigation, Log, TEXT("Marking tiles to update %fms (%d invokers)"), (UpdateEndTime - UpdateStartTime) * 1000, InvokerLocations.Num());
#endif
// once per second
NextInvokersUpdateTime = CurrentTime + ActiveTilesUpdateInterval;
}
}
void UNavigationSystemV1::RegisterNavigationInvoker(AActor* Invoker, float TileGenerationRadius, float TileRemovalRadius)
{
if (Invoker != nullptr)
{
RegisterInvoker(*Invoker, TileGenerationRadius, TileRemovalRadius);
}
}
void UNavigationSystemV1::UnregisterNavigationInvoker(AActor* Invoker)
{
if (Invoker != nullptr)
{
UnregisterInvoker(*Invoker);
}
}
//----------------------------------------------------------------------//
// DEPRECATED
//----------------------------------------------------------------------//
FVector UNavigationSystemV1::ProjectPointToNavigation(UObject* WorldContextObject, const FVector& Point, ANavigationData* NavData, TSubclassOf<UNavigationQueryFilter> FilterClass, const FVector QueryExtent)
{
FNavLocation ProjectedPoint(Point);
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys)
{
ANavigationData* UseNavData = NavData ? NavData : NavSys->GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
if (UseNavData)
{
NavSys->ProjectPointToNavigation(Point, ProjectedPoint, QueryExtent.IsNearlyZero() ? INVALID_NAVEXTENT : QueryExtent, UseNavData,
UNavigationQueryFilter::GetQueryFilter(*UseNavData, WorldContextObject, FilterClass));
}
}
return ProjectedPoint.Location;
}
FVector UNavigationSystemV1::GetRandomReachablePointInRadius(UObject* WorldContextObject, const FVector& Origin, float Radius, ANavigationData* NavData, TSubclassOf<UNavigationQueryFilter> FilterClass)
{
FNavLocation RandomPoint;
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys)
{
ANavigationData* UseNavData = NavData ? NavData : NavSys->GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
if (UseNavData)
{
NavSys->GetRandomReachablePointInRadius(Origin, Radius, RandomPoint, UseNavData, UNavigationQueryFilter::GetQueryFilter(*UseNavData, WorldContextObject, FilterClass));
}
}
return RandomPoint.Location;
}
FVector UNavigationSystemV1::GetRandomPointInNavigableRadius(UObject* WorldContextObject, const FVector& Origin, float Radius, ANavigationData* NavData, TSubclassOf<UNavigationQueryFilter> FilterClass)
{
FNavLocation RandomPoint;
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(World);
if (NavSys)
{
ANavigationData* UseNavData = NavData ? NavData : NavSys->GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
if (UseNavData)
{
NavSys->GetRandomPointInNavigableRadius(Origin, Radius, RandomPoint, UseNavData, UNavigationQueryFilter::GetQueryFilter(*UseNavData, WorldContextObject, FilterClass));
}
}
return RandomPoint.Location;
}
void UNavigationSystemV1::SimpleMoveToActor(AController* Controller, const AActor* Goal)
{
UE_LOG(LogNavigation, Error, TEXT("SimpleMoveToActor is deprecated. Use UAIBlueprintHelperLibrary::SimpleMoveToActor instead"));
}
void UNavigationSystemV1::SimpleMoveToLocation(AController* Controller, const FVector& Goal)
{
UE_LOG(LogNavigation, Error, TEXT("SimpleMoveToLocation is deprecated. Use UAIBlueprintHelperLibrary::SimpleMoveToLocation instead"));
}
//----------------------------------------------------------------------//
// NEW STUFF!
//----------------------------------------------------------------------//
void UNavigationSystemV1::VerifyNavigationRenderingComponents(const bool bShow)
{
// make sure nav mesh has a rendering component
ANavigationData* const NavData = GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
if (NavData && NavData->RenderingComp == nullptr)
{
NavData->RenderingComp = NavData->ConstructRenderingComponent();
if (NavData->RenderingComp)
{
NavData->RenderingComp->SetVisibility(bShow);
NavData->RenderingComp->RegisterComponent();
}
}
if (NavData == nullptr)
{
UE_LOG(LogNavigation, Warning, TEXT("No NavData found when calling UNavigationSystemV1::VerifyNavigationRenderingComponents()"));
}
}
#if !UE_BUILD_SHIPPING
void UNavigationSystemV1::GetOnScreenMessages(TMultiMap<FCoreDelegates::EOnScreenMessageSeverity, FText>& OutMessages)
{
// check navmesh
#if WITH_EDITOR
const bool bIsNavigationAutoUpdateEnabled = UNavigationSystemV1::GetIsNavigationAutoUpdateEnabled();
#else
const bool bIsNavigationAutoUpdateEnabled = true;
#endif
if (IsNavigationDirty()
&& ((OperationMode == FNavigationSystemRunMode::EditorMode && !bIsNavigationAutoUpdateEnabled)
|| !SupportsNavigationGeneration() || !CanRebuildDirtyNavigation()))
{
OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Error
, LOCTEXT("NAVMESHERROR", "NAVMESH NEEDS TO BE REBUILT"));
}
}
#endif // !UE_BUILD_SHIPPING
INavigationDataInterface* UNavigationSystemV1::GetNavDataForActor(const AActor& Actor)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(Actor.GetWorld());
ANavigationData* NavData = nullptr;
const INavAgentInterface* AsNavAgent = CastChecked<INavAgentInterface>(&Actor);
if (AsNavAgent)
{
const FNavAgentProperties& AgentProps = AsNavAgent->GetNavAgentPropertiesRef();
NavData = NavSys->GetNavDataForProps(AgentProps);
}
if (NavData == nullptr)
{
NavData = NavSys->GetDefaultNavDataInstance(FNavigationSystem::DontCreate);
}
// Only RecastNavMesh supported
return (INavigationDataInterface*)(Cast<ARecastNavMesh>(NavData));
}
int UNavigationSystemV1::GetNavigationBoundsForNavData(const ANavigationData& NavData, TArray<FBox>& OutBounds) const
{
const int InitialBoundsCount = OutBounds.Num();
OutBounds.Reserve(InitialBoundsCount + RegisteredNavBounds.Num());
const int32 AgentIndex = GetSupportedAgentIndex(&NavData);
for (const FNavigationBounds& NavigationBounds : RegisteredNavBounds)
{
if (NavigationBounds.SupportedAgents.Contains(AgentIndex))
{
OutBounds.Add(NavigationBounds.AreaBox);
}
}
return OutBounds.Num() - InitialBoundsCount;
}
const FNavDataConfig& UNavigationSystemV1::GetDefaultSupportedAgent()
{
static const FNavDataConfig DefaultAgent;
const UNavigationSystemV1* NavSysCDO = GetDefault<UNavigationSystemV1>();
check(NavSysCDO);
return NavSysCDO->SupportedAgents.Num() > 0
? NavSysCDO->GetDefaultSupportedAgentConfig()
: DefaultAgent;
}
void UNavigationSystemV1::OverrideSupportedAgents(const TArray<FNavDataConfig>& NewSupportedAgents)
{
UE_CLOG(bWorldInitDone, LogNavigation, Warning, TEXT("Trying to override NavigationSystem\'s SupportedAgents past the World\'s initialization"));
SupportedAgents = NewSupportedAgents;
if (SupportedAgents.Num() == 0)
{
SupportedAgents.Add(FNavDataConfig(FNavigationSystem::FallbackAgentRadius, FNavigationSystem::FallbackAgentHeight));
}
}
void UNavigationSystemV1::Configure(const UNavigationSystemConfig& Config)
{
}
//----------------------------------------------------------------------//
// UNavigationSystemModuleConfig
//----------------------------------------------------------------------//
UNavigationSystemModuleConfig::UNavigationSystemModuleConfig(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UNavigationSystemModuleConfig::PostInitProperties()
{
Super::PostInitProperties();
const UNavigationSystemV1* NavSysCDO = GetDefault<UNavigationSystemV1>();
if (NavSysCDO)
{
UpdateWithNavSysCDO(*NavSysCDO);
}
}
void UNavigationSystemModuleConfig::UpdateWithNavSysCDO(const UNavigationSystemV1& NavSysCDO)
{
UClass* MyClass = NavigationSystemClass.ResolveClass();
if (MyClass != nullptr && MyClass->IsChildOf(NavSysCDO.GetClass()))
{
bStrictlyStatic = NavSysCDO.bStaticRuntimeNavigation;
bCreateOnClient = NavSysCDO.bAllowClientSideNavigation;
bAutoSpawnMissingNavData = NavSysCDO.bAutoCreateNavigationData;
bSpawnNavDataInNavBoundsLevel = NavSysCDO.bSpawnNavDataInNavBoundsLevel;
}
}
UNavigationSystemBase* UNavigationSystemModuleConfig::CreateAndConfigureNavigationSystem(UWorld& World) const
{
if (bCreateOnClient == false && World.GetNetMode() == NM_Client)
{
return nullptr;
}
UNavigationSystemBase* NewNavSys = Super::CreateAndConfigureNavigationSystem(World);
UNavigationSystemV1* NavSysInstance = Cast<UNavigationSystemV1>(NewNavSys);
UE_CLOG(NavSysInstance == nullptr && NewNavSys != nullptr, LogNavigation, Error
, TEXT("Unable to spawn navsys instance of class %s - unable to cast to UNavigationSystemV1")
, *NavigationSystemClass.GetAssetName()
);
if (NavSysInstance)
{
NavSysInstance->bAutoCreateNavigationData = bAutoSpawnMissingNavData;
NavSysInstance->bSpawnNavDataInNavBoundsLevel = bSpawnNavDataInNavBoundsLevel;
if (bStrictlyStatic)
{
NavSysInstance->ConfigureAsStatic();
}
}
return NavSysInstance;
}
#if WITH_EDITOR
void UNavigationSystemModuleConfig::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
static const FName NAME_NavigationSystemClass = GET_MEMBER_NAME_CHECKED(UNavigationSystemConfig, NavigationSystemClass);
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property)
{
FName PropName = PropertyChangedEvent.Property->GetFName();
if (PropName == NAME_NavigationSystemClass)
{
if (NavigationSystemClass.IsValid() == false)
{
NavigationSystemClass = *GEngine->NavigationSystemClass;
}
else
{
NavigationSystemClass.TryLoad();
TSubclassOf<UNavigationSystemBase> NavSysClass = NavigationSystemClass.ResolveClass();
const UNavigationSystemV1* NavSysCDO = *NavSysClass
? NavSysClass->GetDefaultObject<UNavigationSystemV1>()
: (UNavigationSystemV1*)nullptr;
if (NavSysCDO)
{
UpdateWithNavSysCDO(*NavSysCDO);
}
}
}
}
}
#endif // WITH_EDITOR
#undef LOCTEXT_NAMESPACE