Files
UnrealEngineUWP/Engine/Source/Editor/UnrealEd/Private/StaticLightingSystem/StaticLightingSystem.cpp
Marc Audy d5628cd986 Copying //UE4/Dev-Framework to //UE4/Dev-Main (Source: //UE4/Dev-Framework @ 3967517)
#rb none
#lockdown Nick.Penwarden
#rnx

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

Change 3804281 by Fred.Kimberley

	Improve contrast on watches in blueprints.

Change 3804322 by Fred.Kimberley

	First pass at adding a watch window for blueprint debugging.

Change 3804737 by mason.seay

	Added some Descriptions to tests that didn't have any, and fixed some typos

Change 3806103 by mason.seay

	Moved and Renamed Timers test map and content appropriately

Change 3806164 by Fred.Kimberley

	Add missing property types to GetDebugInfoInternal.

	#jira UE-53355

Change 3806617 by Dan.Oconnor

	Function Terminator (and derived types) now use FMemberReference instead of a UClass/FName pair. This fixes various bugs when resolving the UFunction referenced by the function terminator

	#jira UE-31754, UE-42431, UE-53315, UE-53172

Change 3808541 by Fred.Kimberley

	Add support for redirecting user defined enums.
	This is in response to the following UDN thread: https://udn.unrealengine.com/questions/404141/is-is-possible-to-create-a-redirector-from-a-bluep.html

Change 3808565 by mason.seay

	Added a few more struct tests

Change 3809840 by mason.seay

	Renamed CharacterMovement.umap to CharacterCollision.  Fixed up content to reflect this change.

Change 3809847 by mason.seay

	Added Object Timer tests.  Fixed up existing timer test to remove delay dependency

Change 3811704 by Ben.Zeigler

	Fix issue where identical enum redirects registered to different initial names would throw an incorrect error, it's fine if the value change maps are identical

Change 3811946 by Ben.Zeigler

	#jira UE-53511 Fix it so it is possible to set a user defined struct value back to it's default. The UDS hack in PropertyValueToString is no longer needed, but this could affect some other user struct editor operations

Change 3812061 by Dan.Oconnor

	Stepping over or in to nodes that are expanded at compile time (e.g. event nodes, spawn actor nodes) no longer requires multiple 'steps'

	#jira UE-52854

Change 3812259 by Dan.Oconnor

	Fix asset broken by removal of an unkown enum

	#jira UE-51419

Change 3812904 by Ben.Zeigler

	Make ResolveRedirects on StreamableManager public as it can be used to validate things

Change 3812958 by Ben.Zeigler

	#jira UE-52977 Fix crashes when binding blueprint editor commands to keys and using from invalid contexts

Change 3812975 by Mieszko.Zielinski

	Added contraptions to catch a rare eidtor-time EQS crash #UE4

	#jira UE-53468

Change 3818530 by Phillip.Kavan

	Fix incorrect access to nested instanced subobjects in nativized Blueprint ctor codegen.

	Change summary:
	- Modified FEmitDefaultValueHelper::HandleInstancedSubobject() to properly reference the outer and check ptr validity when creating/obtaining nested default subobjects.
	- Modified FEmitDefaultValueHelper::HandleClassSubobject() to better guard against code generation based on an invalid local variable name.

	#jira UE-52167

Change 3819733 by Mieszko.Zielinski

	Marked UAISenseConfig_Blueprint and UAISense_Blueprint as hidedropdown #UE4

	#jira UE-15089

Change 3821776 by Marc.Audy

	Remove redundent code in SpawnActorFromClass that already exists in ConstructObjectFromClass parent class

Change 3823851 by mason.seay

	Moved and renamed blueprints used for Object Reference testing

Change 3824165 by Phillip.Kavan

	Ensure that subobject class types are constructed prior to accessing a subobject CDO in a nativized Blueprint class's generated ctor at runtime.

	Change summary:
	- Modified FFakeImportTableHelper to tag subobject class types as a preload dependency of the outer converted Blueprint class type and not of the CDO.

	#jira UE-53111

Change 3830309 by mason.seay

	Created Literal Gameplay Tag Container test

Change 3830562 by Phillip.Kavan

	Blueprint nativization bug fixes (reviewed/taken from PR).

	Change summary:
	- Modified FSafeContextScopedEmitter::ValidationChain() to ensure that generated code calls the global IsValid() utility function on objects.
	- Modified FBlueprintCompilerCppBackend::EmitCreateArrayStatement() to generate a proper cast on MakeArray node inputs for enum class types.
	- Modified FBlueprintCompilerCppBackend::EnimCallStatementInner() to more correctly identify an interface function call site.
	- Modified FEmitHelper::GenerateAutomaticCast() to properly handle automatic casts of enum arrays.
	- (Modified from PR source) Added new FComponentDataUtils statics to consolidate custom init code generation for converted special-case component types (e.g. BodyInstance). Ties native component DSOs to the same pre/post as converted non-native component templates around the OuterGenerate() loop.
	- Modified FExposeOnSpawnValidator::IsSupported() to include CPT_SoftObjectReference property types.
	- Modified UBlueprintGeneratedClass::CheckAndApplyComponentTemplateOverrides() to no longer break out of the loop before finding additional ICH override record matches.

	#4202

	#jira UE-52188

Change 3830579 by Fred.Kimberley

	Add support for turning off multiple watches at once in the watch window.

	#jira UE-53852

Change 3836047 by Zak.Middleton

	#ue4 - Dev test maps for overlaps perf tests.

Change 3836768 by Phillip.Kavan

	Fix for a build failure that could occur with Blueprint nativization enabled and EDL disabled. This was a regression introduced in 4.18.

	Change summary:
	- Modified FEmitDefaultValueHelper::AddStaticFunctionsForDependencies() to emit the correct signature for constructing FBlueprintDependencyData elements when the EDL boot time optimization is disabled.

	#jira UE-53908

Change 3838085 by mason.seay

	Functional tests around basic blueprint functions

Change 3840489 by Ben.Zeigler

	#jira UE-31662 Fix regression with renaming parent inherited function. It was not correctly searching the parent's skeleton class during the child's recompile so it was erroneously detecting the parent function as missing

Change 3840648 by mason.seay

	Updated Descriptions on tests

Change 3842914 by Ben.Zeigler

	Improve comments around stremable handle cancel/release

Change 3850413 by Ben.Zeigler

	Fix asset registry memory reporting, track some newer fields and correctly report the state size instead of static size twice
	Copy of CL #3849610

Change 3850426 by Ben.Zeigler

	Reduce asset registry memory in cooked build by stripping out searchable names and empty dependency nodes by default
	Add option to strip dependency data for asset data with no tags, this was always true before but isn't necessarily safe
	Copy of CL #3850389

Change 3853449 by Phillip.Kavan

	Fix a scoping issue for local instanced subobject references in nativized Blueprint C++ code. Also, don't emit redundant assignment statements for instanced subobject reference properties.

	Change summary:
	- Consolidated FComponentDataUtils into FDefaultSubobjectData and extended FNonativeComponentData from it in order to handle both native & non-native DSO initialization codegen through a more common interface.
	- Exposed FEmitDefaultValueHelper::HandleInstancedSubobject() as a public API and added a 'SubobjectData' parameter to allow initialization codegen to be deferred until after all default subobjects have been mapped to local variables within the current scope.
	- Modified FEmitDefaultValueHelper::GenerateConstructor() to first map all default subobjects to local variables and then emit any delta initialization code for property values.
	- Modified FEmitDefaultValueHelper::HandleSpecialTypes() to return an empty string for an instanced reference to a default subobject. This allows us to avoid emitting initialization statements to unnecessarily reassign instances back to the same property.
	- Modified FEmitDefaultValueHelper::InnerGenerate() to better handle instanced references to default subobjects, ensuring that we don't emit unnecessary assignment statements and array initialization code to the converted class constructor in C++.
	- Fixed a few typos.

	#jira UE-53960

Change 3853465 by Phillip.Kavan

	Fix plugin module C++ source template to conform to recent public include path changes.

Change 3857599 by Marc.Audy

	PR #4438: UE-54281: Make None a valid default value to select (Contributed by projectgheist)
	#jira UE-54281
	#jira UE-54399

Change 3863259 by Zak.Middleton

	#ue4 - Save bandwidth for replicated characters by only replicating 4 byte timestamp value to clients if it's actually needed for Linear smoothing. Added option to always replicate the timestamp ("bNetworkAlwaysReplicateTransformUpdateTimestamp", default off), in case users still want this timestamp for some reason, or if smoothing mode changes dynamically and the server won't know.

	#jira UE-46293

Change 3863491 by Zak.Middleton

	#ue4 - Reduce network RPC overhead for players that are not moving. Added ClientNetSendMoveDeltaTimeStationary (default 12Hz) to supplement existing ClientNetSendMoveDeltaTime and ClientNetSendMoveDeltaTimeThrottled. UCharacterMovementComponent::GetClientNetSendDeltaTime() now uses this time if Acceleration and Velocity are zero, and the control rotation matches the last ack'd control rotation from the server.

	Also fixed up code default for ClientNetSendMoveDeltaTime to match default INI value.

	#jira UE-21264

Change 3865325 by Zak.Middleton

	#ue4 - Fix static analysis warning about possible null PC pointer.

	#jira none

Change 3869828 by Ben.Zeigler

	#jira UE-54786 Fix it so -cookonthefly cooperates with -iterate by writing out a development asset registry

Change 3869969 by mason.seay

	Character Movement Functional Tests

Change 3870099 by Mason.Seay

	Submitted asset deletes

Change 3870105 by mason.seay

	Removed link to anim blueprint to fix errors

Change 3870238 by mason.seay

	Test map for Async Loading in a Loop

Change 3870479 by Ben.Zeigler

	Add code to check CoreRedirects for SoftObjectPaths when saving or resolving in the editor. This is a bit slow so we don't want to do it on load
	We don't have any good way to know the type of a path so I check both Object and Class redirectors, which will also pickup Module renames

Change 3875224 by mason.seay

	Functional tests for Event BeginPlay execution order

Change 3875409 by mason.seay

	Optimized and fixed up character movement tests (because a potential bug in FunctionalTestActor is always passing a test when it can fail)

Change 3878947 by Mieszko.Zielinski

	CIS fixes #UE4

Change 3879000 by Mieszko.Zielinski

	More CIS fixes #UE4

Change 3879139 by Mieszko.Zielinski

	Even moar CIS fixes #UE4

Change 3879742 by mason.seay

	Added animation to Nativization Widget asset

Change 3880198 by Zak.Middleton

	#ue4 - CanCrouchInCurrentState() returns false when character capsule is simulating physics.

	#jira UE-54875
	github #4479

Change 3880266 by Zak.Middleton

	#ue4 - Optimize UpdateCharacterStateBeforeMovement() to do cheaper tests earlier (avoid CanCrouchInCurrentState() unless necessary, now that it tests IsSimulatingPhysics() which is not trivial).

	#jira UE-54875

Change 3881546 by Mieszko.Zielinski

	*.Build.cs files clean up - removed redundant dependencies from NavigationSystem and AIModule #UE4

Change 3881547 by Mieszko.Zielinski

	Removed a bunch of DEPRECATED functions from the new NavigationSystem module #UE4

	Removed all deprecates prior 4.15 (picked this one because I do know some licencees are still using it).

Change 3881742 by mason.seay

	Additional crouch test to cover UE-54875

Change 3881794 by Mieszko.Zielinski

	Fixed a bug in FVisualLoggerHelpers::GetCategories resulting in losing verbosity information #UE4

Change 3884503 by Mieszko.Zielinski

	Fixed TopDown code template to make it compile after navsys refactor #UE4

	#jira UE-55039

Change 3884507 by Mieszko.Zielinski

	Switched ensures in UNavigationSystemV1:SimpleMoveToX to error-level logs #UE4

	It's an error rather than a warning because the functions no longer do anything. Making it work would require a cyclic dependency between NavigationSystem and AIModule.

	#jira UE-55033

Change 3884594 by Mieszko.Zielinski

	Added a const FNavigationSystem::GetCurrent version #UE4

	lack of it was causing KiteDemo to not compile.

Change 3884602 by Mieszko.Zielinski

	Mac editor compilation fix #UE4

Change 3884615 by Mieszko.Zielinski

	Fixed FAIDataProviderValue::GetRawValuePtr not being accessible from outside of AIModule #UE4

Change 3885254 by Mieszko.Zielinski

	Guessfix for UE-55030 #UE4

	The name of NavigationSystem module was put in wrong in the IMPLEMENT_MODULE macro

	#jira 55030

Change 3885286 by Mieszko.Zielinski

	Changed how NavigationSystem module includes DerivedDataCache module #UE4

	#jira UE-55035

Change 3885492 by mason.seay

	Minor tweaks to animation

Change 3885773 by mason.seay

	Resaving assets to clear out warning

Change 3886433 by Mieszko.Zielinski

	Fixed TP_TopDownBP's player controller BP to not use deprecated nav functions #UE4

	#jira UE-55108

Change 3886783 by Mieszko.Zielinski

	Removed silly inclusion of NavigationSystemTypes.h from NavigationSystemTypes.h #UE4

Change 3887019 by Mieszko.Zielinski

	Fixed accessing unchecked pointer in ANavigationData::OnNavAreaAdded #UE4

Change 3891031 by Mieszko.Zielinski

	Fixed missing includes in NavigationSystem.cpp #UE4

Change 3891037 by Mieszko.Zielinski

	ContentEample's navigation fix #UE4

	#jira UE-55109

Change 3891044 by Mieszko.Zielinski

	PR #4456: Fix bug in UAISense_Sight::OnListenerForgetsActor (Contributed by maxtunel)

	#UE4

Change 3891598 by mason.seay

	Resaving assets to clear out "empty engine version" spam

Change 3891612 by mason.seay

	Fixed deprecated Set Text warnings

Change 3893334 by Mieszko.Zielinski

	Fixed a bug in navmesh generation resulting in not removing layers that ended up empty after rebuilding #UE4

	#jira UE-55041

Change 3893394 by Mieszko.Zielinski

	Fixed navmesh debug drawing to properly display octree elements with "per instance transforms" (like instanced SMs) #UE4

	Also, added a more detailed debug drawing of navoctree contents (optional, but on by default).

Change 3893395 by Mieszko.Zielinski

	Added a bit of code to navigation system's initialization that checks the enegine ini for sections refering to the moved navigation classes, and complain about it #UE4

	The message is printed as an error-level log line and it says what should the offending section be renamed to.

Change 3895563 by Dan.Oconnor

	Mirror 3895535
	Append history from previous branches in source control history view

	#jira none

Change 3896930 by Mieszko.Zielinski

	Added an option to tick navigation system while the game is paused #UE4

	Controlled via NavigationSystemV1.bTickWhilePaused, ini- and ProjectSettings-configurable.

	#jira UE-39275

Change 3897554 by Mieszko.Zielinski

	Unified how NavMeshRenderingComponent draws navmesh and octree collision's polys #UE4

Change 3897556 by Mieszko.Zielinski

	Fixed what kind of nav tile bounds we're sending to nav-colliding elements when calling 'per-instance transform' delegate #UE4

	#jira UE-45261

Change 3898064 by Mieszko.Zielinski

	Made SM Editor display AI-navigation-related whenever bHasNavigationData is set to true #UE4

	#jira UE-50436

Change 3899004 by Mieszko.Zielinski

	Fixed UEnvQueryItemType_Actor::GetItemLocation and UEnvQueryItemType_Actor::GetItemRotation to return FAISystem::InvalidLocation and FAISystem::InvalidRotation respectively instead of '0' when hosted Actor ptr is null #UE4

	Note for programmers: this changes the default behavior of this edge case. You might want to go through your code and check if you're comparing UEnvQueryItemType_Actor::GetItem*'s results to 0.

Change 3901733 by Mieszko.Zielinski

	Made FEnvQueryInstance::PrepareContext implementations returning vectors and rotators ignore InvalidLocation and InvalidRotation (respectively) #UE4

Change 3901925 by Ben.Zeigler

	#jira UE-55395 Fix issue where the cooker could load asset registry caches made in -game that do not have dependency data, leading to broken cooks

Change 3902166 by Marc.Audy

	Make ULevel::GetWorld final

Change 3902749 by Ben.Zeigler

	Fix it so pressing refresh button in asset audit window actually refreshes the asset management database

Change 3902763 by Ben.Zeigler

	#jira UE-55407 Fix it so editor tutorials are not cooked unless referenced, by correctly marking soft object paths imported from editor project settings as editor-only

Change 3905578 by Phillip.Kavan

	The UX to add a new parameter on a Blueprint delegate is now at parity with Blueprint functions.

	#4392

	#jira UE-53779

Change 3905848 by Phillip.Kavan

	First pass of the experimental Blueprint graph bookmarks feature.

	#jira UE-10052

Change 3906025 by Phillip.Kavan

	CIS fix.

Change 3906195 by Phillip.Kavan

	Add missing icon file.

Change 3906356 by Phillip.Kavan

	Moved Blueprint bookmarks enable flag into EditorExperimentalSettings for consistency with other options.

Change 3910628 by Ben.Zeigler

	Partial fix for UE-55363, this allows references to ObjectRedirectors to be switched from parent class to a child class on load as this should always be safe
	This does not actually fix UE-55363 because that case is changing from UMaterial to UMaterialInstanceConstant, and those are siblings instead of parent/child

Change 3912470 by Ben.Zeigler

	#jira UE-55586 Fix issue with saving redirected soft object paths where the export sort could accidentally cause the parent CDO to get modified between name tagging and writing exports, which is unsafe because due to delta serialization it would try to write names that were not previously tagged

Change 3913045 by Marc.Audy

	Fix issues where recursion in to child actors wasn't being handled correctly

Change 3913398 by Fred.Kimberley

	Fixes a misspelled name for one of the classes in the ability system.

	PR #4430: Fixed spelling of FGameplayAbilityInputBinds. (Contributed by IntegralLee)
	#github

	#jira UE-54327

Change 3918016 by Fred.Kimberley

	Ensure AllocGameplayEffectContext is being used in all cases where FGameplayeEffectContext is being created.

	#jira UE-52668

	PR #4250: Only create FGameplayEffectContext via AbilitySystemGlobals::.AllocGameplayEffectContext (Contributed by slonopotamus)
	#github

Change 3924653 by Mieszko.Zielinski

	Fixed LoadEngineClass local to UnrealEngine.cpp to check class redirects before falling back to default class instance #UE4

	#jira UE-55378

Change 3925614 by Phillip.Kavan

	Fix ForEachEnum node to skip over hidden enum values in new placements by default.

	Change summary:
	- Added FKismetNodeHelperLibrary::ShouldHideEnumeratorIndex() as an internal-only Blueprint node support API.
	- Modified FForExpandNodeHelper::AllocateDefaultPins() to add a "Skip Hidden" input pin (advanced). Pin default value is false.
	- Added a UK2Node_ForEachElementInEnum::PostPlacedNewNode() override to set the default value of the "Skip Hidden" input pin to 'true' for all new node placements.
	- Modified UK2Node_ForEachElementInEnum::ExpandNode() to include additional expansion logic based on the "Skip Hidden" input pin. For new placements (i.e. when the pin defaults to 'true'), an intermediate branch node will now be inserted into the compiled execution sequence to test for "hidden" metadata on the value before executing the loop body. If the input pin is linked, another intermediate branch will be inserted into the execution sequence prior to the "hidden" metadata test. All existing placements of the node will remain as-is after compilation (i.e. no additional intermediate branch nodes will be included in the expansion).

	#jira UE-34563

Change 3925649 by Marc.Audy

	Fix up issue post merge from Main with navigation system refactor

Change 3926293 by Phillip.Kavan

	Temp fix to unblock CIS.

	#jira UE-34563

Change 3926523 by Marc.Audy

	Ensure that a renamed Actor is in the correct Actors array

	#jira UE-46718

Change 3928732 by Fred.Kimberley

	Unshelved from pending changelist '3793298':

	#jira UE-53136

	PR #4287: virtual additions for AttributeSet extendability (Contributed by TWIDan)
	#github

Change 3928780 by Marc.Audy

	PR #4309: The display names of the functions. (Contributed by SertacOgan)
	#jira UE-53334

Change 3929730 by Joseph.Wysosky

	Submitting test assets for the new Blueprint Structure test cases

Change 3931919 by Joseph.Wysosky

	Deleting BasicStructure asset to rest MemberVariables back to default settings

Change 3931922 by Joseph.Wysosky

	Adding BasicStructure test asset back with default members

Change 3932083 by Phillip.Kavan

	Fix Compositing plugin source files to conform to updated relative include path specifications.

	- Encountered while testing Blueprint nativization of assets with dependencies on Composure/LensDistortion APIs.

Change 3932196 by Dan.Oconnor

	Resetting a property to default now uses the same codepath as assigning the value from the slate control

	#jira UE-55909

Change 3932408 by Lukasz.Furman

	fixed behavior tree services attached to task nodes being sometimes recognized as root level
	#jira nope

Change 3932808 by Marc.Audy

	PR #4083: Change to UK2Node_BaseAsyncTask to have pin tooltips on latent nodes (Contributed by dwrpayne)
	#jira UE-50871

Change 3934101 by Phillip.Kavan

	Revise ForEachEnum node expansion logic to exclude hidden values at compile time.

	Change summary:
	- Removed UKismetNodeHelperLibrary::ShouldHideEnumeratorIndex() (no longer in use).
	- Modified UK2Node_ForEachElementInEnum::ExpandNode() to include an enum switch node in the expansion, which will exclude hidden values when constructed. The additional expansion will occur if the enum type contains at least one hidden value.

	#jira UE-34563

Change 3934106 by Phillip.Kavan

	Mirrored 4.19 fixes to allow for EngineTest iteration w/ nativization enabled.

	Change summary:
	- Mirrored CLs 3876918, 3878968, 3883257, 3885566, 3912161 and 3920519.

Change 3934116 by Phillip.Kavan

	UBT: Explicitly define the DEPRECATED_FORGAME macro only for non-engine modules.

	Change summary:
	- Modified UEBuildModule.SetupPrivateCompileEnvironment() to check the 'bTreatAsEngineModule' flag from the rules assembly rather than testing the module's build type.

Change 3934382 by Phillip.Kavan

	Avoid inclusion of monolothic engine header files in nativized Blueprint codegen.

Change 3936387 by Mieszko.Zielinski

	Added a flag to NavModifierComponent to control whether agent's height is being used while expadning modifier's bounds during navmesh generation #UE4

Change 3936905 by Ben.Marsh

	Disable IncludeTool warning for DEPRECATED_FORGAME macro; we expect this to be different for game modules.

Change 3940537 by Marc.Audy

	Don't allow maps, sets, or arrays with an actor inner type in user defined structs to select an actor from the currently open level as default value.
	#jira UE-55938

Change 3940901 by Marc.Audy

	Properly name CVar global to reflect what it is for

Change 3943043 by Marc.Audy

	Fix world context functions not being able to be used in CheatManager derived blueprints
	#jira UE-55787

Change 3943075 by Mieszko.Zielinski

	Moved path-following related delegats' interface from NavigationSystemBase over to a new IPathFollowingManagerInterface #UE4

Change 3943089 by Mieszko.Zielinski

	Fixed how WorldSettings.NavigationSystemConfig gets created #UE4

	Made it so that there's always a NavigationSystemConfig instance present, but added a 'Null' config - this was required due to issues with creation/serialization of instanced subobjects.
	The change required adding copying constructors to FNavAgentProperties and FNavDataConfig.
	Also, fixed FNavAgentProperties.IsEquivalent to be symetrical.

Change 3943225 by Marc.Audy

	Fix spelling of Implements

Change 3950813 by Marc.Audy

	Include owner in attachment mismatch ensure
	#jira UE-56148

Change 3950996 by Marc.Audy

	Fix cases where bit packed properties used the entire byte not just the bit when interacting with boolean arrays

	#jira UE-55482

Change 3952086 by Marc.Audy

	PR #4483: Add Missing Radial Damage Multicast Delegate (Contributed by error454)
	#jira UE-54974

Change 3952720 by Marc.Audy

	PR #4575: Check if *Pawn* is a null Pointer (Contributed by dani9bma)
	#jira UE-56248

Change 3952804 by Richard.Hinckley

	Changes to BP API export commandlet to support better plugin exporting. Contributed by Harry Wang of Google.

Change 3952962 by Marc.Audy

	UHT now validates that ExpandEnumAsExecs references a valid parameter to the function.
	#jira UE-49610

Change 3952977 by Phillip.Kavan

	Fix EDL cycle at load time in nativized cooked builds when a circular dependency exists between converted and unconverted assets.

	Change summary:
	- Added FGatherConvertedClassDependencies::MarkUnconvertedClassAsNecessary().
	- Modified FFindAssetsToInclude::MaybeIncludeObjectAsDependency() to mark unconverted BPGCs (e.g. DOBPs) as necessary for conversion when the potential for a circular dependency exists so that we generate stub wrappers rather than depend on them directly.
	- Fixed a few typos in existing API names.

	#jira UE-48233

Change 3953658 by Marc.Audy

	(4.19.1) Fix inserting a reroute node causing connections to break on a GetClassDefaults node
	#jira UE-56270

Change 3954727 by Marc.Audy

	Add friendly name to custom version mismatch message

Change 3954906 by Marc.Audy

	(4.19.1) Fix crash when undoing changes related to reroute nodes connected to a GetClassDefaults node
	#jira UE-56313

Change 3954997 by Marc.Audy

	Ensure and return null if GetOuter<WithinClass> is called on a CDO for uclasses declared as within another so we don't get a UPackage c-style cast to the expected outer type

Change 3955091 by Marc.Audy

	Do not register subcomponents that are not auto register
	#jira UE-52878

Change 3955943 by Marc.Audy

	Make AbilitySystemComponent pass parameters by const& instead of ref as no state is being changed

Change 3956185 by Zak.Middleton

	#ue4 - Fix Characters using scoped movement updates (the default) not visually rotating when rotated at small rates at high framerate.

	This was caused by FScopedMovementUpdate::IsTransformDirty() using a larger FTransform comparison tolerance than USceneComponent::UpdateComponentToWorldWithParent().

	#jira none

Change 3958102 by Marc.Audy

	Clean out dead code path from k2node_select
	Select node now resets pins to wildcard if none of the pins are in use

Change 3958113 by Lukasz.Furman

	added OnSearchStart call to root level behavior tree services
	#jira UE-56257

Change 3958361 by Marc.Audy

	Fix literal input pins on select being set to wildcard during compilation

Change 3961148 by Dan.Oconnor

	Mirror 3961139 from Release 4.19
	Fix for placeholder objects being left behind when loading certain UMG assets - this could causea crash when loading UMG assets
	#jira UE-55742

Change 3961640 by Marc.Audy

	Select node now displays Add Pin button
	Undo of changing select node index type now works correctly.
	Connections to option pins now maintained across change of index pin type
	#jira UE-20742

Change 3962262 by Marc.Audy

	Display "Object Reference" instead of "Object Object Reference" and "Soft Object Reference" instead of "Object Soft Object Reference"

Change 3962795 by Phillip.Kavan

	Fix for a crash when cooking with Blueprint nativization enabled after encountering a nested instanced editor-only default subobject inherited from a native C++ base class.

	- Mirrored from //UE4/Release-4.19 (3962782)

	#jira UE-56316

Change 3962991 by Marc.Audy

	Modify Negate/Increment/Decrement Int/Float so that the output is always the desired result even if a non-mutable pin is passed in.
	Note that this can mean the result being returned and the value of the pin passed in if queried again will not be the same (in the case of pure nodes).
	#jira UE-54807

Change 3963114 by Marc.Audy

	Fix ensures/crash as a result of UClass expecting to be able to access the UPackage of CDOs via the GetOuterUPackage call.

Change 3963427 by Marc.Audy

	Fix initialization order
	Initialize bUseBackwardsCompatForEmptyAutogeneratedValue

Change 3963781 by Marc.Audy

	Fix without editor compiles

Change 3964576 by Marc.Audy

	PR #4599: : Working category for timelines (Contributed by projectgheist)
	#jira UE-56460
	#jira UE-26053

Change 3964782 by Dan.Oconnor

	Mirror 3964772 from Release 4.19

	Fix crash when force deleting certain blueprints, we can only check for authoritativeness while reinstancing

	#jira UE-56447

Change 3965156 by Mieszko.Zielinski

	PR #4592: Visual Logger optimization to fix rapid FPS drop when many items are hidden (Contributed by tstaples)

	#jira UE-56435

Change 3965173 by Marc.Audy

	(4.19.1) Fix incorrectly switching a cooling down tick to be an enabled tick when marking it enabled.
	#jira UE-56431

Change 3966117 by Marc.Audy

	Fix select nodes inside macros using wildcard array inputs having issues resolving type.
	#jira UE-56484

Change 3878901 by Mieszko.Zielinski

	NavigationSystem's code refactored out of the engine and into a new separate module #UE4

	The CL contains required changes to all of our internal projects. Fortnite and Paragon have been tested, while the rest have been only compiled.

Change 3879409 by Mieszko.Zielinski

	Further fallout fixes after ripping out NavigationSystem out of the engine #UE4

	- Fixed bad ini redirects (had NavigationSystem.NavigationSystem instead of NavigationSystem.NavigationSystemV1)
	- Added missing FNavigationSystem::GetDefaultNavDataClass binding (resulting in QAGame's func tests failing)

Change 3897655 by Ben.Zeigler

	#jira UE-55211 Fix it so literal soft object pins on blueprint nodes get correctly cooked/referenced
	It now sets the thread context to skip internal serialize and calls the archive's serialize function instead of bypassing it, which allows it to pick up references

	Change 3962780 by Marc.Audy

	When preventing a split pin from being orphaned, all sub pins must also be prevented.
	#jira UE-56328
	Repack members of UEdGraphPin to avoid wasted space (saves 16bytes)

[CL 3967553 by Marc Audy in Main branch]
2018-03-27 14:27:07 -04:00

2424 lines
87 KiB
C++

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
StaticLightingSystem.cpp: Bsp light mesh illumination builder code
=============================================================================*/
#include "CoreMinimal.h"
#include "Misc/MessageDialog.h"
#include "HAL/FileManager.h"
#include "Misc/Paths.h"
#include "Misc/Guid.h"
#include "Misc/ConfigCacheIni.h"
#include "HAL/IConsoleManager.h"
#include "Misc/ScopedSlowTask.h"
#include "Misc/App.h"
#include "Modules/ModuleManager.h"
#include "UObject/ObjectMacros.h"
#include "UObject/GarbageCollection.h"
#include "Layout/Visibility.h"
#include "Framework/Application/SlateApplication.h"
#include "Engine/EngineTypes.h"
#include "GameFramework/Actor.h"
#include "Components/PrimitiveComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Components/LightComponentBase.h"
#include "Components/ReflectionCaptureComponent.h"
#include "AI/NavigationSystemBase.h"
#include "Engine/MapBuildDataRegistry.h"
#include "Components/LightComponent.h"
#include "Model.h"
#include "Engine/Brush.h"
#include "Misc/PackageName.h"
#include "Editor/EditorEngine.h"
#include "Settings/EditorExperimentalSettings.h"
#include "Settings/LevelEditorMiscSettings.h"
#include "Engine/Texture2D.h"
#include "Misc/FeedbackContext.h"
#include "UObject/UObjectHash.h"
#include "UObject/UObjectIterator.h"
#include "GameFramework/WorldSettings.h"
#include "Engine/GeneratedMeshAreaLight.h"
#include "Components/SkyLightComponent.h"
#include "Components/ModelComponent.h"
#include "Engine/LightMapTexture2D.h"
#include "Editor.h"
#include "Engine/Selection.h"
#include "EditorModeManager.h"
#include "EditorModes.h"
#include "Dialogs/Dialogs.h"
FSwarmDebugOptions GSwarmDebugOptions;
#include "Lightmass/LightmassCharacterIndirectDetailVolume.h"
#include "Lightmass/VolumetricLightmapDensityVolume.h"
#include "StaticLighting.h"
#include "StaticLightingSystem/StaticLightingPrivate.h"
#include "ModelLight.h"
#include "Engine/LevelStreaming.h"
#include "LevelUtils.h"
#include "EngineModule.h"
#include "LightMap.h"
#include "ShadowMap.h"
#include "EditorBuildUtils.h"
#include "ComponentRecreateRenderStateContext.h"
#include "Engine/LODActor.h"
DEFINE_LOG_CATEGORY(LogStaticLightingSystem);
#include "EngineGlobals.h"
#include "Toolkits/AssetEditorManager.h"
#include "Lightmass/LightmassImportanceVolume.h"
#include "Components/LightmassPortalComponent.h"
#include "Lightmass/Lightmass.h"
#include "StatsViewerModule.h"
#include "Logging/TokenizedMessage.h"
#include "Logging/MessageLog.h"
#include "Framework/Notifications/NotificationManager.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Misc/UObjectToken.h"
#define LOCTEXT_NAMESPACE "StaticLightingSystem"
/** The number of hardware threads to not use for building static lighting. */
#define NUM_STATIC_LIGHTING_UNUSED_THREADS 0
bool GbLogAddingMappings = false;
/** Counts the number of lightmap textures generated each lighting build. */
extern ENGINE_API int32 GLightmapCounter;
/** Whether to compress lightmaps. Reloaded from ini each lighting build. */
extern ENGINE_API bool GCompressLightmaps;
/** Whether to allow lighting builds to generate streaming lightmaps. */
extern ENGINE_API bool GAllowStreamingLightmaps;
// NOTE: We're only counting the top-level mip-map for the following variables.
/** Total number of texels allocated for all lightmap textures. */
extern ENGINE_API uint64 GNumLightmapTotalTexels;
/** Total number of texels used if the texture was non-power-of-two. */
extern ENGINE_API uint64 GNumLightmapTotalTexelsNonPow2;
/** Number of lightmap textures generated. */
extern ENGINE_API int32 GNumLightmapTextures;
/** Total number of mapped texels. */
extern ENGINE_API uint64 GNumLightmapMappedTexels;
/** Total number of unmapped texels. */
extern ENGINE_API uint64 GNumLightmapUnmappedTexels;
/** Whether to allow cropping of unmapped borders in lightmaps and shadowmaps. Controlled by BaseEngine.ini setting. */
extern ENGINE_API bool GAllowLightmapCropping;
/** Total lightmap texture memory size (in bytes), including GLightmapTotalStreamingSize. */
extern ENGINE_API uint64 GLightmapTotalSize;
/** Total memory size for streaming lightmaps (in bytes). */
extern ENGINE_API uint64 GLightmapTotalStreamingSize;
/** Largest boundingsphere radius to use when packing lightmaps into a texture atlas. */
extern ENGINE_API float GMaxLightmapRadius;
/** Total number of texels allocated for all shadowmap textures. */
extern ENGINE_API uint64 GNumShadowmapTotalTexels;
/** Number of shadowmap textures generated. */
extern ENGINE_API int32 GNumShadowmapTextures;
/** Total number of mapped texels. */
extern ENGINE_API uint64 GNumShadowmapMappedTexels;
/** Total number of unmapped texels. */
extern ENGINE_API uint64 GNumShadowmapUnmappedTexels;
/** Total shadowmap texture memory size (in bytes), including GShadowmapTotalStreamingSize. */
extern ENGINE_API uint64 GShadowmapTotalSize;
/** Total texture memory size for streaming shadowmaps. */
extern ENGINE_API uint64 GShadowmapTotalStreamingSize;
/** If non-zero, purge old lightmap data when rebuilding lighting. */
int32 GPurgeOldLightmaps=1;
static FAutoConsoleVariableRef CVarPurgeOldLightmaps(
TEXT("PurgeOldLightmaps"),
GPurgeOldLightmaps,
TEXT("If non-zero, purge old lightmap data when rebuilding lighting.")
);
int32 GMultithreadedLightmapEncode = 1;
static FAutoConsoleVariableRef CVarMultithreadedLightmapEncode(TEXT("r.MultithreadedLightmapEncode"), GMultithreadedLightmapEncode, TEXT("Lightmap encoding after rebuild lightmaps is done multithreaded."));
int32 GMultithreadedShadowmapEncode = 1;
static FAutoConsoleVariableRef CVarMultithreadedShadowmapEncode(TEXT("r.MultithreadedShadowmapEncode"), GMultithreadedShadowmapEncode, TEXT("Shadowmap encoding after rebuild lightmaps is done multithreaded."));
TSharedPtr<FStaticLightingManager> FStaticLightingManager::StaticLightingManager;
TSharedPtr<FStaticLightingManager> FStaticLightingManager::Get()
{
if (!StaticLightingManager.IsValid())
{
StaticLightingManager = MakeShareable(new FStaticLightingManager);
}
return StaticLightingManager;
}
void FStaticLightingManager::ProcessLightingData()
{
auto StaticLightingSystem = FStaticLightingManager::Get()->ActiveStaticLightingSystem;
check(StaticLightingSystem);
FNavigationLockContext NavUpdateLock(StaticLightingSystem->GetWorld(), ENavigationLockReason::LightingUpdate);
bool bSuccessful = StaticLightingSystem->FinishLightmassProcess();
FEditorDelegates::OnLightingBuildKept.Broadcast();
if (!bSuccessful)
{
FStaticLightingManager::Get()->FailLightingBuild();
}
FStaticLightingManager::Get()->ClearCurrentNotification();
}
void FStaticLightingManager::CancelLightingBuild()
{
if (FStaticLightingManager::Get()->ActiveStaticLightingSystem->IsAsyncBuilding())
{
GEditor->SetMapBuildCancelled( true );
FStaticLightingManager::Get()->ClearCurrentNotification();
FEditorDelegates::OnLightingBuildFailed.Broadcast();
}
else
{
FStaticLightingManager::Get()->FailLightingBuild();
}
}
void FStaticLightingManager::SendProgressNotification()
{
// Start the lightmass 'progress' notification
FNotificationInfo Info( LOCTEXT("LightBuildMessage", "Building lighting") );
Info.bFireAndForget = false;
Info.ButtonDetails.Add(FNotificationButtonInfo(
LOCTEXT("LightBuildCancel","Cancel"),
LOCTEXT("LightBuildCancelToolTip","Cancels the lighting build in progress."),
FSimpleDelegate::CreateStatic(&FStaticLightingManager::CancelLightingBuild)));
LightBuildNotification = FSlateNotificationManager::Get().AddNotification(Info);
if (LightBuildNotification.IsValid())
{
LightBuildNotification.Pin()->SetCompletionState(SNotificationItem::CS_Pending);
}
}
void FStaticLightingManager::ClearCurrentNotification()
{
if ( LightBuildNotification.IsValid() )
{
LightBuildNotification.Pin()->SetCompletionState(SNotificationItem::CS_None);
LightBuildNotification.Pin()->ExpireAndFadeout();
LightBuildNotification.Reset();
}
}
void FStaticLightingManager::SetNotificationText( FText Text )
{
if ( LightBuildNotification.IsValid() )
{
LightBuildNotification.Pin()->SetText( Text );
}
}
void FStaticLightingManager::ImportRequested()
{
if (FStaticLightingManager::Get()->ActiveStaticLightingSystem)
{
FStaticLightingManager::Get()->ActiveStaticLightingSystem->CurrentBuildStage = FStaticLightingSystem::ImportRequested;
}
}
void FStaticLightingManager::DiscardRequested()
{
if (FStaticLightingManager::Get()->ActiveStaticLightingSystem)
{
FStaticLightingManager::Get()->ClearCurrentNotification();
FStaticLightingManager::Get()->ActiveStaticLightingSystem->CurrentBuildStage = FStaticLightingSystem::Finished;
}
}
void FStaticLightingManager::SendBuildDoneNotification( bool AutoApplyFailed )
{
FText CompletedText = LOCTEXT("LightBuildDoneMessage", "Lighting build completed");
if (ActiveStaticLightingSystem != StaticLightingSystems.Last().Get() && ActiveStaticLightingSystem->LightingScenario)
{
FString PackageName = FPackageName::GetShortName(ActiveStaticLightingSystem->LightingScenario->GetOutermost()->GetName());
CompletedText = FText::Format(LOCTEXT("LightScenarioBuildDoneMessage", "{0} Lighting Scenario completed"), FText::FromString(PackageName));
}
FNotificationInfo Info(CompletedText);
Info.bFireAndForget = false;
Info.bUseThrobber = false;
FNotificationButtonInfo ApplyNow = FNotificationButtonInfo(
LOCTEXT( "LightBuildKeep", "Apply Now" ),
LOCTEXT( "LightBuildKeepToolTip", "Keeps and applies built lighting data." ),
FSimpleDelegate::CreateStatic( &FStaticLightingManager::ImportRequested ) );
ApplyNow.VisibilityOnSuccess = EVisibility::Collapsed;
FNotificationButtonInfo Discard = FNotificationButtonInfo(
LOCTEXT( "LightBuildDiscard", "Discard" ),
LOCTEXT( "LightBuildDiscardToolTip", "Ignores the built lighting data generated." ),
FSimpleDelegate::CreateStatic( &FStaticLightingManager::DiscardRequested ) );
Discard.VisibilityOnSuccess = EVisibility::Collapsed;
Info.ButtonDetails.Add( ApplyNow );
Info.ButtonDetails.Add( Discard );
FEditorDelegates::OnLightingBuildSucceeded.Broadcast();
LightBuildNotification = FSlateNotificationManager::Get().AddNotification( Info );
if ( LightBuildNotification.IsValid() )
{
LightBuildNotification.Pin()->SetCompletionState( AutoApplyFailed ? SNotificationItem::CS_Pending : SNotificationItem::CS_Success );
}
}
void FStaticLightingManager::CreateStaticLightingSystem(const FLightingBuildOptions& Options)
{
if (StaticLightingSystems.Num() == 0)
{
check(!ActiveStaticLightingSystem);
for (ULevel* Level : GWorld->GetLevels())
{
if (Level->bIsLightingScenario && Level->bIsVisible)
{
StaticLightingSystems.Emplace(new FStaticLightingSystem(Options, GWorld, Level));
}
}
if (StaticLightingSystems.Num() == 0)
{
StaticLightingSystems.Emplace(new FStaticLightingSystem(Options, GWorld, nullptr));
}
ActiveStaticLightingSystem = StaticLightingSystems[0].Get();
bool bSuccess = ActiveStaticLightingSystem->BeginLightmassProcess();
if (bSuccess)
{
SendProgressNotification();
}
else
{
FStaticLightingManager::Get()->FailLightingBuild();
}
}
else
{
// Tell the user that they must close their current build first.
FStaticLightingManager::Get()->FailLightingBuild(
LOCTEXT("LightBuildInProgressWarning", "A lighting build is already in progress! Please cancel it before triggering a new build."));
}
}
void FStaticLightingManager::UpdateBuildLighting()
{
if (ActiveStaticLightingSystem != NULL)
{
// Note: UpdateLightingBuild can change ActiveStaticLightingSystem
ActiveStaticLightingSystem->UpdateLightingBuild();
if (ActiveStaticLightingSystem && ActiveStaticLightingSystem->CurrentBuildStage == FStaticLightingSystem::Finished)
{
ActiveStaticLightingSystem = nullptr;
StaticLightingSystems.RemoveAt(0);
if (StaticLightingSystems.Num() > 0)
{
ActiveStaticLightingSystem = StaticLightingSystems[0].Get();
bool bSuccess = ActiveStaticLightingSystem->BeginLightmassProcess();
if (bSuccess)
{
SendProgressNotification();
}
else
{
FStaticLightingManager::Get()->FailLightingBuild();
}
}
}
if (!ActiveStaticLightingSystem)
{
FinishLightingBuild();
}
}
}
void FStaticLightingManager::FailLightingBuild( FText ErrorText)
{
FStaticLightingManager::Get()->ClearCurrentNotification();
if (GEditor->GetMapBuildCancelled())
{
ErrorText = LOCTEXT("LightBuildCanceledMessage", "Lighting build canceled.");
}
else
{
// Override failure message if one provided
if (ErrorText.IsEmpty())
{
ErrorText = LOCTEXT("LightBuildFailedMessage", "Lighting build failed.");
}
}
FNotificationInfo Info( ErrorText );
Info.ExpireDuration = 4.f;
FEditorDelegates::OnLightingBuildFailed.Broadcast();
LightBuildNotification = FSlateNotificationManager::Get().AddNotification(Info);
if (LightBuildNotification.IsValid())
{
LightBuildNotification.Pin()->SetCompletionState(SNotificationItem::CS_Fail);
}
UE_LOG(LogStaticLightingSystem, Warning, TEXT("Failed to build lighting!!! %s"),*ErrorText.ToString());
FMessageLog("LightingResults").Open();
DestroyStaticLightingSystems();
}
void FStaticLightingManager::FinishLightingBuild()
{
UWorld* World = GWorld;
GetRendererModule().UpdateMapNeedsLightingFullyRebuiltState(World);
GEngine->DeferredCommands.AddUnique(TEXT("MAP CHECK NOTIFYRESULTS"));
if (World->Scene)
{
// Everything should be built at this point, dump unbuilt interactions for debugging
World->Scene->DumpUnbuiltLightInteractions(*GLog);
}
GEditor->BuildReflectionCaptures(World);
}
void FStaticLightingManager::DestroyStaticLightingSystems()
{
ActiveStaticLightingSystem = NULL;
StaticLightingSystems.Empty();
}
bool FStaticLightingManager::IsLightingBuildCurrentlyRunning() const
{
return ActiveStaticLightingSystem != NULL;
}
bool FStaticLightingManager::IsLightingBuildCurrentlyExporting() const
{
return ActiveStaticLightingSystem != NULL && ActiveStaticLightingSystem->IsAmortizedExporting();
}
FStaticLightingSystem::FStaticLightingSystem(const FLightingBuildOptions& InOptions, UWorld* InWorld, ULevel* InLightingScenario)
: Options(InOptions)
, bBuildCanceled(false)
, DeterministicIndex(0)
, NextVisibilityId(0)
, CurrentBuildStage(FStaticLightingSystem::NotRunning)
, World(InWorld)
, LightingScenario(InLightingScenario)
, LightmassProcessor(NULL)
{
}
FStaticLightingSystem::~FStaticLightingSystem()
{
if (LightmassProcessor)
{
delete LightmassProcessor;
}
}
bool FStaticLightingSystem::BeginLightmassProcess()
{
StartTime = FPlatformTime::Seconds();
CurrentBuildStage = FStaticLightingSystem::Startup;
bool bRebuildDirtyGeometryForLighting = true;
bool bForceNoPrecomputedLighting = false;
GDebugStaticLightingInfo = FDebugLightingOutput();
{
FLightmassStatistics::FScopedGather StartupStatScope(LightmassStatistics.StartupTime);
// Flip the results page
FFormatNamedArguments Arguments;
Arguments.Add(TEXT("TimeStamp"), FText::AsDateTime(FDateTime::Now()));
FText LightingResultsPageName(FText::Format(LOCTEXT("LightingResultsPageName", "Lighting Build - {TimeStamp}"), Arguments));
FMessageLog("LightingResults").NewPage(LightingResultsPageName);
FStatsViewerModule& StatsViewerModule = FModuleManager::Get().LoadModuleChecked<FStatsViewerModule>(TEXT("StatsViewer"));
StatsViewerModule.GetPage(EStatsPage::LightingBuildInfo)->Clear();
GLightmapCounter = 0;
GNumLightmapTotalTexels = 0;
GNumLightmapTotalTexelsNonPow2 = 0;
GNumLightmapTextures = 0;
GNumLightmapMappedTexels = 0;
GNumLightmapUnmappedTexels = 0;
GLightmapTotalSize = 0;
GLightmapTotalStreamingSize = 0;
GNumShadowmapTotalTexels = 0;
GNumShadowmapTextures = 0;
GNumShadowmapMappedTexels = 0;
GNumShadowmapUnmappedTexels = 0;
GShadowmapTotalSize = 0;
GShadowmapTotalStreamingSize = 0;
for( TObjectIterator<UPrimitiveComponent> It ; It ; ++It )
{
UPrimitiveComponent* Component = *It;
Component->VisibilityId = INDEX_NONE;
}
FString SkippedLevels;
for ( int32 LevelIndex=0; LevelIndex < World->GetNumLevels(); LevelIndex++ )
{
ULevel* Level = World->GetLevel(LevelIndex);
if (ShouldOperateOnLevel(Level))
{
Level->LightmapTotalSize = 0.0f;
Level->ShadowmapTotalSize = 0.0f;
ULevelStreaming* LevelStreaming = NULL;
if ( World->PersistentLevel != Level )
{
LevelStreaming = FLevelUtils::FindStreamingLevel( Level );
}
if (!Options.ShouldBuildLightingForLevel(Level))
{
if (SkippedLevels.Len() > 0)
{
SkippedLevels += FString(TEXT(", "));
}
SkippedLevels += Level->GetName();
GatherBuildDataResourcesToKeep(Level);
}
}
else if (Level && !Level->bIsLightingScenario && !Level->bIsVisible)
{
GatherBuildDataResourcesToKeep(Level);
}
}
for (ULevelStreaming* CurStreamingLevel : World->GetStreamingLevels())
{
if (CurStreamingLevel && CurStreamingLevel->GetLoadedLevel() && !CurStreamingLevel->GetShouldBeVisibleInEditor())
{
if (SkippedLevels.Len() > 0)
{
SkippedLevels += FString(TEXT(", ")) + CurStreamingLevel->GetWorldAssetPackageName();
}
else
{
SkippedLevels += CurStreamingLevel->GetWorldAssetPackageName();
}
}
}
if (SkippedLevels.Len() > 0 && !IsRunningCommandlet())
{
// Warn when some levels are not visible and therefore will not be built, because that indicates that only a partial build will be done,
// Lighting will still be unbuilt for some areas when playing through the level.
const FText SkippedLevelsWarning = FText::Format( LOCTEXT("SkippedLevels", "The following levels will not have the lighting rebuilt because of your selected lighting build options: {0}"), FText::FromString( SkippedLevels ) );
FSuppressableWarningDialog::FSetupInfo Info( SkippedLevelsWarning, LOCTEXT("SkippedLevelsDialogTitle", "Rebuild Lighting - Warning" ), "WarnOnHiddenLevelsBeforeRebuild" );
Info.ConfirmText = LOCTEXT("SkippedWarningConfirm", "Build");
FSuppressableWarningDialog WarnAboutSkippedLevels( Info );
WarnAboutSkippedLevels.ShowModal();
}
static const auto AllowStaticLightingVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting"));
const bool bAllowStaticLighting = (!AllowStaticLightingVar || AllowStaticLightingVar->GetValueOnGameThread() != 0);
bForceNoPrecomputedLighting = World->GetWorldSettings()->bForceNoPrecomputedLighting || !bAllowStaticLighting;
GConfig->GetFloat( TEXT("TextureStreaming"), TEXT("MaxLightmapRadius"), GMaxLightmapRadius, GEngineIni );
GConfig->GetBool( TEXT("TextureStreaming"), TEXT("AllowStreamingLightmaps"), GAllowStreamingLightmaps, GEngineIni );
if (!bForceNoPrecomputedLighting)
{
// Begin the static lighting progress bar.
GWarn->BeginSlowTask( LOCTEXT("BeginBuildingStaticLightingTaskStatus", "Building lighting"), false );
}
else
{
UE_LOG(LogStaticLightingSystem, Warning, TEXT("WorldSettings.bForceNoPrecomputedLighting is true, Skipping Lighting Build!"));
}
FConfigCacheIni::LoadGlobalIniFile(GLightmassIni, TEXT("Lightmass"), NULL, true);
verify(GConfig->GetBool(TEXT("DevOptions.StaticLighting"), TEXT("bUseBilinearFilterLightmaps"), GUseBilinearLightmaps, GLightmassIni));
verify(GConfig->GetBool(TEXT("DevOptions.StaticLighting"), TEXT("bAllowCropping"), GAllowLightmapCropping, GLightmassIni));
verify(GConfig->GetBool(TEXT("DevOptions.StaticLighting"), TEXT("bRebuildDirtyGeometryForLighting"), bRebuildDirtyGeometryForLighting, GLightmassIni));
verify(GConfig->GetBool(TEXT("DevOptions.StaticLighting"), TEXT("bCompressLightmaps"), GCompressLightmaps, GLightmassIni));
GCompressLightmaps = GCompressLightmaps && World->GetWorldSettings()->LightmassSettings.bCompressLightmaps;
GAllowLightmapPadding = true;
FMemory::Memzero(&LightingMeshBounds, sizeof(FBox));
FMemory::Memzero(&AutomaticImportanceVolumeBounds, sizeof(FBox));
GLightingBuildQuality = Options.QualityLevel;
}
{
FLightmassStatistics::FScopedGather CollectStatScope(LightmassStatistics.CollectTime);
// Prepare lights for rebuild.
{
FLightmassStatistics::FScopedGather PrepareStatScope(LightmassStatistics.PrepareLightsTime);
if (!Options.bOnlyBuildVisibility)
{
// Delete all AGeneratedMeshAreaLight's, since new ones will be created after the build with updated properties.
USelection* EditorSelection = GEditor->GetSelectedActors();
for(TObjectIterator<AGeneratedMeshAreaLight> LightIt;LightIt;++LightIt)
{
if (ShouldOperateOnLevel((*LightIt)->GetLevel()))
{
if (EditorSelection)
{
EditorSelection->Deselect(*LightIt);
}
(*LightIt)->GetWorld()->DestroyActor(*LightIt);
}
}
for (TObjectIterator<ULightComponentBase> LightIt(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::PendingKill); LightIt; ++LightIt)
{
ULightComponentBase* const Light = *LightIt;
const bool bLightIsInWorld = Light->GetOwner()
&& World->ContainsActor(Light->GetOwner())
&& !Light->GetOwner()->IsPendingKill();
if (bLightIsInWorld && ShouldOperateOnLevel(Light->GetOwner()->GetLevel()))
{
if (Light->bAffectsWorld
&& Light->IsRegistered()
&& (Light->HasStaticShadowing() || Light->HasStaticLighting()))
{
// Make sure the light GUIDs are up-to-date.
Light->ValidateLightGUIDs();
// Add the light to the system's list of lights in the world.
Lights.Add(Light);
}
}
}
}
}
{
FLightmassStatistics::FScopedGather GatherStatScope(LightmassStatistics.GatherLightingInfoTime);
if (IsTexelDebuggingEnabled())
{
// Clear reference to the selected lightmap
GCurrentSelectedLightmapSample.Lightmap = NULL;
}
GatherStaticLightingInfo(bRebuildDirtyGeometryForLighting, bForceNoPrecomputedLighting);
}
// Sort the mappings - and tag meshes if doing deterministic mapping
if (GLightmassDebugOptions.bSortMappings)
{
struct FCompareNumTexels
{
FORCEINLINE bool operator()( const FStaticLightingMappingSortHelper& A, const FStaticLightingMappingSortHelper& B ) const
{
return B.NumTexels < A.NumTexels;
}
};
UnSortedMappings.Sort( FCompareNumTexels() );
for (int32 SortIndex = 0; SortIndex < UnSortedMappings.Num(); SortIndex++)
{
FStaticLightingMapping* Mapping = UnSortedMappings[SortIndex].Mapping;
Mappings.Add(Mapping);
if (Mapping->bProcessMapping)
{
if (Mapping->Mesh)
{
Mapping->Mesh->Guid = FGuid(0,0,0,DeterministicIndex++);
}
}
}
UnSortedMappings.Empty();
}
// Verify deterministic lighting setup, if it is enabled...
for (int32 CheckMapIdx = 0; CheckMapIdx < Mappings.Num(); CheckMapIdx++)
{
if (Mappings[CheckMapIdx]->bProcessMapping)
{
FGuid CheckGuid = Mappings[CheckMapIdx]->Mesh->Guid;
if ((CheckGuid.A != 0) ||
(CheckGuid.B != 0) ||
(CheckGuid.C != 0) ||
(CheckGuid.D >= (uint32)(Mappings.Num()))
)
{
UE_LOG(LogStaticLightingSystem, Warning, TEXT("Lightmass: Error in deterministic lighting for %s:%s"),
*(Mappings[CheckMapIdx]->Mesh->Guid.ToString()), *(Mappings[CheckMapIdx]->GetDescription()));
}
}
}
// if we are dumping binary results, clear up any existing ones
if (Options.bDumpBinaryResults)
{
FStaticLightingSystem::ClearBinaryDumps();
}
}
ProcessingStartTime = FPlatformTime::Seconds();
bool bLightingSuccessful = false;
if (!bForceNoPrecomputedLighting)
{
bool bSavedUpdateStatus_LightMap = FLightMap2D::GetStatusUpdate();
if (GLightmassDebugOptions.bImmediateProcessMappings)
{
FLightMap2D::SetStatusUpdate(false);
}
bLightingSuccessful = CreateLightmassProcessor();
if (bLightingSuccessful)
{
GatherScene();
bLightingSuccessful = InitiateLightmassProcessor();
}
if (GLightmassDebugOptions.bImmediateProcessMappings)
{
FLightMap2D::SetStatusUpdate(bSavedUpdateStatus_LightMap);
}
}
else
{
InvalidateStaticLighting();
ApplyNewLightingData(true);
}
if (!bForceNoPrecomputedLighting)
{
// End the static lighting progress bar.
GWarn->EndSlowTask();
}
return bLightingSuccessful;
}
void FStaticLightingSystem::InvalidateStaticLighting()
{
FLightmassStatistics::FScopedGather InvalidationScopeStat(LightmassStatistics.InvalidationTime);
for( int32 LevelIndex=0; LevelIndex<World->GetNumLevels(); LevelIndex++ )
{
bool bMarkLevelDirty = false;
ULevel* Level = World->GetLevel(LevelIndex);
if (!ShouldOperateOnLevel(Level))
{
continue;
}
const bool bBuildLightingForLevel = Options.ShouldBuildLightingForLevel( Level );
if (bBuildLightingForLevel)
{
if (!Options.bOnlyBuildVisibility)
{
Level->ReleaseRenderingResources();
if (Level->MapBuildData)
{
Level->MapBuildData->InvalidateStaticLighting(World, &BuildDataResourcesToKeep);
}
}
if (Level == World->PersistentLevel)
{
Level->PrecomputedVisibilityHandler.Invalidate(World->Scene);
Level->PrecomputedVolumeDistanceField.Invalidate(World->Scene);
}
// Mark any existing cached lightmap data as transient. This allows the derived data cache to purge it more aggressively.
// It is safe to do so even if some of these lightmaps are needed. It just means compressed data will have to be retrieved
// from the network cache or rebuilt.
if (GPurgeOldLightmaps != 0 && Level->MapBuildData)
{
UPackage* MapDataPackage = Level->MapBuildData->GetOutermost();
for (TObjectIterator<ULightMapTexture2D> It; It; ++It)
{
ULightMapTexture2D* LightMapTexture = *It;
if (LightMapTexture->GetOutermost() == MapDataPackage)
{
LightMapTexture->MarkPlatformDataTransient();
}
}
}
}
}
}
void UpdateStaticLightingHLODTreeIndices(TMultiMap<AActor*, FStaticLightingMesh*>& ActorMeshMap, ALODActor* LODActor, uint32 HLODTreeIndex, uint32& HLODLeafIndex)
{
check(LODActor && HLODTreeIndex > 0);
uint32 LeafStartIndex = HLODLeafIndex;
++HLODLeafIndex;
for (AActor* SubActor : LODActor->SubActors)
{
if (ALODActor* LODSubActor = Cast<ALODActor>(SubActor))
{
UpdateStaticLightingHLODTreeIndices(ActorMeshMap, LODSubActor, HLODTreeIndex, HLODLeafIndex);
}
else
{
TArray<FStaticLightingMesh*> SubActorMeshes;
ActorMeshMap.MultiFind(SubActor,SubActorMeshes);
for (FStaticLightingMesh* SubActorMesh : SubActorMeshes)
{
if (SubActorMesh->HLODTreeIndex == 0)
{
SubActorMesh->HLODTreeIndex = HLODTreeIndex;
SubActorMesh->HLODChildStartIndex = HLODLeafIndex;
SubActorMesh->HLODChildEndIndex = HLODLeafIndex;
++HLODLeafIndex;
}
else
{
// Output error to message log containing tokens to the problematic objects
FMessageLog("LightingResults").Warning()
->AddToken(FUObjectToken::Create(SubActorMesh->Component->GetOwner()))
->AddToken(FTextToken::Create(LOCTEXT("LightmassError_InvalidHLODTreeIndex", "will not be correctly lit since it is part of another Hierarchical LOD cluster besides ")))
->AddToken(FUObjectToken::Create(LODActor));
}
}
}
}
TArray<FStaticLightingMesh*> LODActorMeshes;
ActorMeshMap.MultiFind(LODActor, LODActorMeshes);
for (FStaticLightingMesh* LODActorMesh : LODActorMeshes)
{
LODActorMesh->HLODTreeIndex = HLODTreeIndex;
LODActorMesh->HLODChildStartIndex = LeafStartIndex;
LODActorMesh->HLODChildEndIndex = HLODLeafIndex - 1;
check(LODActorMesh->HLODChildEndIndex >= LODActorMesh->HLODChildStartIndex);
}
}
void FStaticLightingSystem::GatherStaticLightingInfo(bool bRebuildDirtyGeometryForLighting, bool bForceNoPrecomputedLighting)
{
uint32 ActorsInvalidated = 0;
uint32 ActorsToInvalidate = 0;
for( int32 LevelIndex=0; LevelIndex<World->GetNumLevels(); LevelIndex++ )
{
ActorsToInvalidate += World->GetLevel(LevelIndex)->Actors.Num();
}
const int32 ProgressUpdateFrequency = FMath::Max<int32>(ActorsToInvalidate / 20, 1);
GWarn->StatusUpdate( ActorsInvalidated, ActorsToInvalidate, LOCTEXT("GatheringSceneGeometryStatus", "Gathering scene geometry...") );
bool bObjectsToBuildLightingForFound = false;
// Gather static lighting info from actor components.
for (int32 LevelIndex = 0; LevelIndex < World->GetNumLevels(); LevelIndex++)
{
bool bMarkLevelDirty = false;
ULevel* Level = World->GetLevel(LevelIndex);
if (!ShouldOperateOnLevel(Level))
{
continue;
}
// If the geometry is dirty and we're allowed to automatically clean it up, do so
if (Level->bGeometryDirtyForLighting)
{
UE_LOG(LogStaticLightingSystem, Warning, TEXT("WARNING: Lighting build detected that geometry needs to be rebuilt to avoid incorrect lighting (due to modifying a lighting property)."));
if (bRebuildDirtyGeometryForLighting)
{
// This will go ahead and clean up lighting on all dirty levels (not just this one)
UE_LOG(LogStaticLightingSystem, Warning, TEXT("WARNING: Lighting build automatically rebuilding geometry.") );
GEditor->Exec(World, TEXT("MAP REBUILD ALLDIRTYFORLIGHTING"));
}
}
const bool bBuildLightingForLevel = Options.ShouldBuildLightingForLevel(Level);
// Gather static lighting info from BSP.
bool bBuildBSPLighting = bBuildLightingForLevel;
TArray<FNodeGroup*> NodeGroupsToBuild;
TArray<UModelComponent*> SelectedModelComponents;
if (bBuildBSPLighting && !Options.bOnlyBuildVisibility)
{
if (Options.bOnlyBuildSelected)
{
UModel* Model = Level->Model;
GLightmassDebugOptions.bGatherBSPSurfacesAcrossComponents = false;
Model->GroupAllNodes(Level, Lights);
bBuildBSPLighting = false;
// Build only selected brushes/surfaces
TArray<ABrush*> SelectedBrushes;
for (int32 ActorIndex = 0; ActorIndex < Level->Actors.Num(); ActorIndex++)
{
AActor* Actor = Level->Actors[ActorIndex];
if (Actor)
{
ABrush* Brush = Cast<ABrush>(Actor);
if (Brush && Brush->IsSelected())
{
SelectedBrushes.Add(Brush);
}
}
}
TArray<int32> SelectedSurfaceIndices;
// Find selected surfaces...
for (int32 SurfIdx = 0; SurfIdx < Model->Surfs.Num(); SurfIdx++)
{
bool bSurfaceSelected = false;
FBspSurf& Surf = Model->Surfs[SurfIdx];
if ((Surf.PolyFlags & PF_Selected) != 0)
{
SelectedSurfaceIndices.Add(SurfIdx);
bSurfaceSelected = true;
}
else
{
int32 DummyIdx;
if (SelectedBrushes.Find(Surf.Actor, DummyIdx) == true)
{
SelectedSurfaceIndices.Add(SurfIdx);
bSurfaceSelected = true;
}
}
if (bSurfaceSelected == true)
{
// Find it's model component...
for (int32 NodeIdx = 0; NodeIdx < Model->Nodes.Num(); NodeIdx++)
{
const FBspNode& Node = Model->Nodes[NodeIdx];
if (Node.iSurf == SurfIdx)
{
UModelComponent* SomeModelComponent = Level->ModelComponents[Node.ComponentIndex];
if (SomeModelComponent)
{
SelectedModelComponents.AddUnique(SomeModelComponent);
for (int32 InnerNodeIndex = 0; InnerNodeIndex < SomeModelComponent->Nodes.Num(); InnerNodeIndex++)
{
FBspNode& InnerNode = Model->Nodes[SomeModelComponent->Nodes[InnerNodeIndex]];
SelectedSurfaceIndices.AddUnique(InnerNode.iSurf);
}
}
}
}
}
}
// Pass 2...
if (SelectedSurfaceIndices.Num() > 0)
{
for (int32 SSIdx = 0; SSIdx < SelectedSurfaceIndices.Num(); SSIdx++)
{
int32 SurfIdx = SelectedSurfaceIndices[SSIdx];
// Find it's model component...
for (int32 NodeIdx = 0; NodeIdx < Model->Nodes.Num(); NodeIdx++)
{
const FBspNode& Node = Model->Nodes[NodeIdx];
if (Node.iSurf == SurfIdx)
{
UModelComponent* SomeModelComponent = Level->ModelComponents[Node.ComponentIndex];
if (SomeModelComponent)
{
SelectedModelComponents.AddUnique(SomeModelComponent);
for (int32 InnerNodeIndex = 0; InnerNodeIndex < SomeModelComponent->Nodes.Num(); InnerNodeIndex++)
{
FBspNode& InnerNode = Model->Nodes[SomeModelComponent->Nodes[InnerNodeIndex]];
SelectedSurfaceIndices.AddUnique(InnerNode.iSurf);
}
}
}
}
}
}
if (SelectedSurfaceIndices.Num() > 0)
{
// Fill in a list of all the node group to rebuild...
bBuildBSPLighting = false;
for (TMap<int32, FNodeGroup*>::TIterator It(Model->NodeGroups); It; ++It)
{
FNodeGroup* NodeGroup = It.Value();
if (NodeGroup && (NodeGroup->Nodes.Num() > 0))
{
for (int32 GroupNodeIdx = 0; GroupNodeIdx < NodeGroup->Nodes.Num(); GroupNodeIdx++)
{
int32 CheckIdx;
if (SelectedSurfaceIndices.Find(Model->Nodes[NodeGroup->Nodes[GroupNodeIdx]].iSurf, CheckIdx) == true)
{
NodeGroupsToBuild.AddUnique(NodeGroup);
bBuildBSPLighting = true;
}
}
}
}
}
}
}
if (bBuildBSPLighting && !bForceNoPrecomputedLighting)
{
if (!Options.bOnlyBuildSelected || Options.bOnlyBuildVisibility)
{
// generate BSP mappings across the whole level
AddBSPStaticLightingInfo(Level, bBuildBSPLighting);
}
else
{
if (NodeGroupsToBuild.Num() > 0)
{
bObjectsToBuildLightingForFound = true;
AddBSPStaticLightingInfo(Level, NodeGroupsToBuild);
}
}
}
// Gather HLOD primitives
TMultiMap<AActor*, UPrimitiveComponent*> PrimitiveActorMap;
TMultiMap<UPrimitiveComponent*, UStaticMeshComponent*> PrimitiveSubStaticMeshMap;
for (int32 ActorIndex = 0; ActorIndex < Level->Actors.Num(); ActorIndex++)
{
AActor* Actor = Level->Actors[ActorIndex];
if (Actor)
{
ALODActor* LODActor = Cast<ALODActor>(Actor);
if (LODActor && LODActor->GetStaticMeshComponent())
{
UPrimitiveComponent* PrimitiveParent = LODActor->GetStaticMeshComponent()->GetLODParentPrimitive();
for (auto SubActor : LODActor->SubActors)
{
PrimitiveActorMap.Add(SubActor, LODActor->GetStaticMeshComponent());
if (PrimitiveParent)
{
PrimitiveActorMap.Add(SubActor, PrimitiveParent);
}
TArray<UStaticMeshComponent*> SubStaticMeshComponents;
SubActor->GetComponents<UStaticMeshComponent>(SubStaticMeshComponents);
for (auto SMC : SubStaticMeshComponents)
{
PrimitiveSubStaticMeshMap.Add(LODActor->GetStaticMeshComponent(), SMC);
}
}
}
}
}
TMultiMap<AActor*, FStaticLightingMesh*> ActorMeshMap;
TArray<ALODActor*> LODActors;
// Gather static lighting info from actors.
for (int32 ActorIndex = 0; ActorIndex < Level->Actors.Num(); ActorIndex++)
{
AActor* Actor = Level->Actors[ActorIndex];
if (Actor)
{
const bool bBuildActorLighting =
bBuildLightingForLevel &&
(!Options.bOnlyBuildSelected || Actor->IsSelected());
TInlineComponentArray<UPrimitiveComponent*> Components;
Actor->GetComponents(Components);
if (bBuildActorLighting)
{
bObjectsToBuildLightingForFound = true;
}
TArray<UPrimitiveComponent*> HLODPrimitiveParents;
PrimitiveActorMap.MultiFind(Actor, HLODPrimitiveParents);
ALODActor* LODActor = Cast<ALODActor>(Actor);
if (LODActor)
{
LODActors.Add(LODActor);
}
// Gather static lighting info from each of the actor's components.
for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++)
{
UPrimitiveComponent* Primitive = Components[ComponentIndex];
if (Primitive->IsRegistered() && !bForceNoPrecomputedLighting)
{
// Find the lights relevant to the primitive.
TArray<ULightComponent*> PrimitiveRelevantLights;
for (int32 LightIndex = 0; LightIndex < Lights.Num(); LightIndex++)
{
ULightComponentBase* LightBase = Lights[LightIndex];
ULightComponent* Light = Cast<ULightComponent>(LightBase);
// Only add enabled lights
if (Light && Light->AffectsPrimitive(Primitive))
{
PrimitiveRelevantLights.Add(Light);
}
}
// Query the component for its static lighting info.
FStaticLightingPrimitiveInfo PrimitiveInfo;
Primitive->GetStaticLightingInfo(PrimitiveInfo, PrimitiveRelevantLights, Options);
if (PrimitiveInfo.Meshes.Num() > 0 && (Primitive->Mobility == EComponentMobility::Static))
{
if (World->GetWorldSettings()->bPrecomputeVisibility)
{
// Make sure the level gets dirtied since we are changing the visibility Id of a component in it
bMarkLevelDirty = true;
}
PrimitiveInfo.VisibilityId = Primitive->VisibilityId = NextVisibilityId;
NextVisibilityId++;
}
TArray<UStaticMeshComponent*> LODSubActorSMComponents;
if (LODActor)
{
PrimitiveSubStaticMeshMap.MultiFind(Primitive, LODSubActorSMComponents);
}
for (auto Mesh : PrimitiveInfo.Meshes)
{
ActorMeshMap.Add(Actor, Mesh);
}
AddPrimitiveStaticLightingInfo(PrimitiveInfo, bBuildActorLighting);
}
}
}
ActorsInvalidated++;
if (ActorsInvalidated % ProgressUpdateFrequency == 0)
{
GWarn->UpdateProgress(ActorsInvalidated, ActorsToInvalidate);
}
}
// Recurse through HLOD trees, group actors and calculate child ranges
uint32 HLODTreeIndex = 1;
uint32 HLODLeafIndex;
for (ALODActor* LODActor : LODActors)
{
// Only process fully merged (root) HLOD nodes
if (LODActor->GetStaticMeshComponent() && !LODActor->GetStaticMeshComponent()->GetLODParentPrimitive())
{
HLODLeafIndex = 0;
UpdateStaticLightingHLODTreeIndices(ActorMeshMap, LODActor, HLODTreeIndex, HLODLeafIndex);
++HLODTreeIndex;
}
}
if (bMarkLevelDirty)
{
Level->MarkPackageDirty();
}
}
if (Options.bOnlyBuildSelected)
{
FMessageLog("LightingResults").Warning(LOCTEXT("LightmassError_BuildSelected", "Building selected actors only, lightmap memory and quality will be sub-optimal until the next full rebuild."));
if (!bObjectsToBuildLightingForFound)
{
FMessageLog("LightingResults").Error(LOCTEXT("LightmassError_BuildSelectedNothingSelected", "Building selected actors and BSP only, but no actors or BSP selected!"));
}
}
}
void FStaticLightingSystem::EncodeTextures(bool bLightingSuccessful)
{
FLightmassStatistics::FScopedGather EncodeStatScope(LightmassStatistics.EncodingTime);
FScopedSlowTask SlowTask(2);
{
FLightmassStatistics::FScopedGather EncodeStatScope2(LightmassStatistics.EncodingLightmapsTime);
// Flush pending shadow-map and light-map encoding.
SlowTask.EnterProgressFrame(1, LOCTEXT("EncodingImportedStaticLightMapsStatusMessage", "Encoding imported static light maps."));
FLightMap2D::EncodeTextures(World, bLightingSuccessful, GMultithreadedLightmapEncode ? true : false);
}
{
FLightmassStatistics::FScopedGather EncodeStatScope2(LightmassStatistics.EncodingShadowMapsTime);
SlowTask.EnterProgressFrame(1, LOCTEXT("EncodingImportedStaticShadowMapsStatusMessage", "Encoding imported static shadow maps."));
FShadowMap2D::EncodeTextures(World, LightingScenario, bLightingSuccessful, GMultithreadedShadowmapEncode ? true : false);
}
}
void FStaticLightingSystem::ApplyNewLightingData(bool bLightingSuccessful)
{
{
FLightmassStatistics::FScopedGather ApplyStatScope(LightmassStatistics.ApplyTime);
// Now that the lighting is done, we can tell the model components to use their new elements,
// instead of the pre-lighting ones
UModelComponent::ApplyTempElements(bLightingSuccessful);
}
{
FLightmassStatistics::FScopedGather FinishStatScope(LightmassStatistics.FinishingTime);
// Mark lights of the computed level to have valid precomputed lighting.
for (int32 LevelIndex = 0; LevelIndex < World->GetNumLevels(); LevelIndex++)
{
ULevel* Level = World->GetLevel(LevelIndex);
if (!ShouldOperateOnLevel(Level))
{
continue;
}
ULevel* StorageLevel = LightingScenario ? LightingScenario : Level;
UMapBuildDataRegistry* Registry = StorageLevel->GetOrCreateMapBuildData();
// Notify level about new lighting data
Level->OnApplyNewLightingData(bLightingSuccessful);
Level->InitializeRenderingResources();
if (World->PersistentLevel == Level)
{
Level->PrecomputedVisibilityHandler.UpdateScene(World->Scene);
Level->PrecomputedVolumeDistanceField.UpdateScene(World->Scene);
}
uint32 ActorCount = Level->Actors.Num();
for (uint32 ActorIndex = 0; ActorIndex < ActorCount; ++ActorIndex)
{
AActor* Actor = Level->Actors[ActorIndex];
if (Actor && bLightingSuccessful && !Options.bOnlyBuildSelected)
{
TInlineComponentArray<ULightComponent*> Components;
Actor->GetComponents(Components);
for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++)
{
ULightComponent* LightComponent = Components[ComponentIndex];
if (LightComponent && (LightComponent->HasStaticShadowing() || LightComponent->HasStaticLighting()))
{
if (!Registry->GetLightBuildData(LightComponent->LightGuid))
{
// Add a dummy entry for ULightComponent::IsPrecomputedLightingValid()
Registry->FindOrAllocateLightBuildData(LightComponent->LightGuid, true);
}
}
}
}
}
const bool bBuildLightingForLevel = Options.ShouldBuildLightingForLevel( Level );
// Store off the quality of the lighting for the level if lighting was successful and we build lighting for this level.
if( bLightingSuccessful && bBuildLightingForLevel )
{
Registry->LevelLightingQuality = Options.QualityLevel;
Registry->MarkPackageDirty();
}
}
// Ensure all primitives which were marked dirty by the lighting build are updated.
// First clear all components so that any references to static lighting assets held
// by scene proxies will be fully released before any components are reregistered.
// We do not rerun construction scripts - nothing should have changed that requires that, and
// want to know which components were not moved during lighting rebuild
{
FGlobalComponentRecreateRenderStateContext RecreateRenderState;
}
// Clean up old shadow-map and light-map data.
CollectGarbage( GARBAGE_COLLECTION_KEEPFLAGS );
// Commit the changes to the world's BSP surfaces.
World->CommitModelSurfaces();
}
// Report failed lighting build (don't count cancelled builds as failure).
if ( !bLightingSuccessful && !bBuildCanceled )
{
FMessageDialog::Open( EAppMsgType::Ok, LOCTEXT("LightingBuildFailedDialogMessage", "The lighting build failed! See the log for more information!") );
}
}
/**
* Reports lighting build statistics to the log.
*/
void FStaticLightingSystem::ReportStatistics()
{
extern UNREALED_API bool GLightmassStatsMode;
if ( GLightmassStatsMode )
{
double TrackedTime =
LightmassStatistics.StartupTime
+ LightmassStatistics.CollectTime
+ LightmassStatistics.ProcessingTime
+ LightmassStatistics.ImportTime
+ LightmassStatistics.ApplyTime
+ LightmassStatistics.EncodingTime
+ LightmassStatistics.InvalidationTime
+ LightmassStatistics.FinishingTime;
double UntrackedTime = LightmassStatistics.TotalTime - TrackedTime;
UE_LOG(LogStaticLightingSystem, Log,
TEXT("Illumination: %s total\n")
TEXT(" %3.1f%%\t%8.1fs Untracked time\n")
, *FPlatformTime::PrettyTime(LightmassStatistics.TotalTime)
, UntrackedTime / LightmassStatistics.TotalTime * 100.0
, UntrackedTime
);
UE_LOG(LogStaticLightingSystem, Log,
TEXT("Breakdown of Illumination time\n")
TEXT(" %3.1f%%\t%8.1fs \tStarting up\n")
TEXT(" %3.1f%%\t%8.1fs \tCollecting\n")
TEXT(" %3.1f%%\t%8.1fs \t--> Preparing lights\n")
TEXT(" %3.1f%%\t%8.1fs \t--> Gathering lighting info\n")
TEXT(" %3.1f%%\t%8.1fs \tProcessing\n")
TEXT(" %3.1f%%\t%8.1fs \tImporting\n")
TEXT(" %3.1f%%\t%8.1fs \tApplying\n")
TEXT(" %3.1f%%\t%8.1fs \tEncoding\n")
TEXT(" %3.1f%%\t%8.1fs \tInvalidating\n")
TEXT(" %3.1f%%\t%8.1fs \tFinishing\n")
, LightmassStatistics.StartupTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.StartupTime
, LightmassStatistics.CollectTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.CollectTime
, LightmassStatistics.PrepareLightsTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.PrepareLightsTime
, LightmassStatistics.GatherLightingInfoTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.GatherLightingInfoTime
, LightmassStatistics.ProcessingTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.ProcessingTime
, LightmassStatistics.ImportTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.ImportTime
, LightmassStatistics.ApplyTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.ApplyTime
, LightmassStatistics.EncodingTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.EncodingTime
, LightmassStatistics.InvalidationTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.InvalidationTime
, LightmassStatistics.FinishingTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.FinishingTime
);
UE_LOG(LogStaticLightingSystem, Log,
TEXT("Breakdown of Processing time\n")
TEXT(" %3.1f%%\t%8.1fs \tCollecting Lightmass scene\n")
TEXT(" %3.1f%%\t%8.1fs \tExporting\n")
TEXT(" %3.1f%%\t%8.1fs \tLightmass\n")
TEXT(" %3.1f%%\t%8.1fs \tSwarm startup\n")
TEXT(" %3.1f%%\t%8.1fs \tSwarm callback\n")
TEXT(" %3.1f%%\t%8.1fs \tSwarm job open\n")
TEXT(" %3.1f%%\t%8.1fs \tSwarm job close\n")
TEXT(" %3.1f%%\t%8.1fs \tImporting\n")
TEXT(" %3.1f%%\t%8.1fs \tApplying\n")
, LightmassStatistics.CollectLightmassSceneTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.CollectLightmassSceneTime
, LightmassStatistics.ExportTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.ExportTime
, LightmassStatistics.LightmassTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.LightmassTime
, LightmassStatistics.SwarmStartupTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.SwarmStartupTime
, LightmassStatistics.SwarmCallbackTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.SwarmCallbackTime
, LightmassStatistics.SwarmJobOpenTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.SwarmJobOpenTime
, LightmassStatistics.SwarmJobCloseTime / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.SwarmJobCloseTime
, LightmassStatistics.ImportTimeInProcessing / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.ImportTimeInProcessing
, LightmassStatistics.ApplyTimeInProcessing / LightmassStatistics.TotalTime * 100.0
, LightmassStatistics.ApplyTimeInProcessing
);
UE_LOG(LogStaticLightingSystem, Log,
TEXT("Breakdown of Export Times\n")
TEXT(" %8.1fs\tVisibility Data\n")
TEXT(" %8.1fs\tVolumetricLightmap Data\n")
TEXT(" %8.1fs\tLights\n")
TEXT(" %8.1fs\tModels\n")
TEXT(" %8.1fs\tStatic Meshes\n")
TEXT(" %8.1fs\tMaterials\n")
TEXT(" %8.1fs\tMesh Instances\n")
TEXT(" %8.1fs\tLandscape Instances\n")
TEXT(" %8.1fs\tMappings\n")
, LightmassStatistics.ExportVisibilityDataTime
, LightmassStatistics.ExportVolumetricLightmapDataTime
, LightmassStatistics.ExportLightsTime
, LightmassStatistics.ExportModelsTime
, LightmassStatistics.ExportStaticMeshesTime
, LightmassStatistics.ExportMaterialsTime
, LightmassStatistics.ExportMeshInstancesTime
, LightmassStatistics.ExportLandscapeInstancesTime
, LightmassStatistics.ExportMappingsTime
);
UE_LOG(LogStaticLightingSystem, Log,
TEXT("Scratch counters\n")
TEXT(" %3.1f%%\tScratch0\n")
TEXT(" %3.1f%%\tScratch1\n")
TEXT(" %3.1f%%\tScratch2\n")
TEXT(" %3.1f%%\tScratch3\n")
, LightmassStatistics.Scratch0
, LightmassStatistics.Scratch1
, LightmassStatistics.Scratch2
, LightmassStatistics.Scratch3
);
float NumLightmapTotalTexels = float(FMath::Max<uint64>(GNumLightmapTotalTexels,1));
float NumShadowmapTotalTexels = float(FMath::Max<uint64>(GNumShadowmapTotalTexels,1));
float LightmapTexelsToMT = float(NUM_HQ_LIGHTMAP_COEF)/float(NUM_STORED_LIGHTMAP_COEF)/1024.0f/1024.0f; // Strip out the SimpleLightMap
float ShadowmapTexelsToMT = 1.0f/1024.0f/1024.0f;
UE_LOG(LogStaticLightingSystem, Log, TEXT("Lightmap textures: %.1f M texels (%.1f%% mapped, %.1f%% unmapped, %.1f%% wasted by packing, %.1f M non-pow2 texels)")
, NumLightmapTotalTexels * LightmapTexelsToMT
, 100.0f * float(GNumLightmapMappedTexels) / NumLightmapTotalTexels
, 100.0f * float(GNumLightmapUnmappedTexels) / NumLightmapTotalTexels
, 100.0f * float(GNumLightmapTotalTexels - GNumLightmapMappedTexels - GNumLightmapUnmappedTexels) / NumLightmapTotalTexels
, GNumLightmapTotalTexelsNonPow2 * LightmapTexelsToMT
);
UE_LOG(LogStaticLightingSystem, Log, TEXT("Shadowmap textures: %.1f M texels (%.1f%% mapped, %.1f%% unmapped, %.1f%% wasted by packing)")
, NumShadowmapTotalTexels * ShadowmapTexelsToMT
, 100.0f * float(GNumShadowmapMappedTexels) / NumShadowmapTotalTexels
, 100.0f * float(GNumShadowmapUnmappedTexels) / NumShadowmapTotalTexels
, 100.0f * float(GNumShadowmapTotalTexels - GNumShadowmapMappedTexels - GNumShadowmapUnmappedTexels) / NumShadowmapTotalTexels
);
for ( int32 LevelIndex=0; LevelIndex < World->GetNumLevels(); LevelIndex++ )
{
ULevel* Level = World->GetLevel(LevelIndex);
UE_LOG(LogStaticLightingSystem, Log, TEXT("Level %2d - Lightmaps: %.1f MB. Shadowmaps: %.1f MB."), LevelIndex, Level->LightmapTotalSize/1024.0f, Level->ShadowmapTotalSize/1024.0f );
}
}
else //if ( GLightmassStatsMode)
{
UE_LOG(LogStaticLightingSystem, Log, TEXT("Illumination: %s (%s encoding lightmaps, %s encoding shadowmaps)"), *FPlatformTime::PrettyTime(LightmassStatistics.TotalTime), *FPlatformTime::PrettyTime(LightmassStatistics.EncodingLightmapsTime), *FPlatformTime::PrettyTime(LightmassStatistics.EncodingShadowMapsTime));
}
UE_LOG(LogStaticLightingSystem, Log, TEXT("Lightmap texture memory: %.1f MB (%.1f MB streaming, %.1f MB non-streaming), %d textures"),
GLightmapTotalSize/1024.0f/1024.0f,
GLightmapTotalStreamingSize/1024.0f/1024.0f,
(GLightmapTotalSize - GLightmapTotalStreamingSize)/1024.0f/1024.0f,
GNumLightmapTextures);
UE_LOG(LogStaticLightingSystem, Log, TEXT("Shadowmap texture memory: %.1f MB (%.1f MB streaming, %.1f MB non-streaming), %d textures"),
GShadowmapTotalSize/1024.0f/1024.0f,
GShadowmapTotalStreamingSize/1024.0f/1024.0f,
(GShadowmapTotalSize - GShadowmapTotalStreamingSize)/1024.0f/1024.0f,
GNumShadowmapTextures);
}
void FStaticLightingSystem::CompleteDeterministicMappings(class FLightmassProcessor* InLightmassProcessor)
{
check(InLightmassProcessor != NULL);
if (InLightmassProcessor && GLightmassDebugOptions.bUseImmediateImport && GLightmassDebugOptions.bImmediateProcessMappings)
{
// Already completed in the Lightmass Run function...
return;
}
double ImportAndApplyStartTime = FPlatformTime::Seconds();
double ApplyTime = 0.0;
int32 CurrentStep = Mappings.Num();
int32 TotalSteps = Mappings.Num() * 2;
const int32 ProgressUpdateFrequency = FMath::Max<int32>(TotalSteps / 20, 1);
GWarn->StatusUpdate( CurrentStep, TotalSteps, LOCTEXT("CompleteDeterministicMappingsStatusMessage", "Importing and applying deterministic mappings...") );
// Process all the texture mappings first...
for (int32 MappingIndex = 0; MappingIndex < Mappings.Num(); MappingIndex++)
{
FStaticLightingTextureMapping* TextureMapping = Mappings[MappingIndex]->GetTextureMapping();
if (TextureMapping)
{
//UE_LOG(LogStaticLightingSystem, Log, TEXT("%32s Completed - %s"), *(TextureMapping->GetDescription()), *(TextureMapping->GetLightingGuid().ToString()));
if (!GLightmassDebugOptions.bUseImmediateImport)
{
InLightmassProcessor->ImportMapping(TextureMapping->GetLightingGuid(), true);
}
else
{
double ApplyStartTime = FPlatformTime::Seconds();
InLightmassProcessor->ProcessMapping(TextureMapping->GetLightingGuid());
ApplyTime += FPlatformTime::Seconds() - ApplyStartTime;
}
}
CurrentStep++;
if (CurrentStep % ProgressUpdateFrequency == 0)
{
GWarn->UpdateProgress(CurrentStep , TotalSteps);
}
}
LightmassStatistics.ImportTimeInProcessing += FPlatformTime::Seconds() - ImportAndApplyStartTime - ApplyTime;
LightmassStatistics.ApplyTimeInProcessing += ApplyTime;
}
struct FCompareByArrayCount
{
FORCEINLINE bool operator()( const TArray<ULightComponent*>& A, const TArray<ULightComponent*>& B ) const
{
// Sort by descending array count
return B.Num() < A.Num();
}
};
/**
* Generates mappings/meshes for all BSP in the given level
*
* @param Level Level to build BSP lighting info for
* @param bBuildLightingForBSP If true, we need BSP mappings generated as well as the meshes
*/
void FStaticLightingSystem::AddBSPStaticLightingInfo(ULevel* Level, bool bBuildLightingForBSP)
{
// For BSP, we aren't Component-centric, so we can't use the GetStaticLightingInfo
// function effectively. Instead, we look across all nodes in the Level's model and
// generate NodeGroups - which are groups of nodes that are coplanar, adjacent, and
// have the same lightmap resolution (henceforth known as being "conodes"). Each
// NodeGroup will get a mapping created for it
// cache the model
UModel* Model = Level->Model;
// reset the number of incomplete groups
Model->NumIncompleteNodeGroups = 0;
Model->CachedMappings.Empty();
Model->bInvalidForStaticLighting = false;
// create all NodeGroups
Model->GroupAllNodes(Level, Lights);
// now we need to make the mappings/meshes
bool bMarkLevelDirty = false;
for (TMap<int32, FNodeGroup*>::TIterator It(Model->NodeGroups); It; ++It)
{
FNodeGroup* NodeGroup = It.Value();
if (NodeGroup->Nodes.Num())
{
// get one of the surfaces/components from the NodeGroup
// @todo UE4: Remove need for GetSurfaceLightMapResolution to take a surfaceindex, or a ModelComponent :)
UModelComponent* SomeModelComponent = Level->ModelComponents[Model->Nodes[NodeGroup->Nodes[0]].ComponentIndex];
int32 SurfaceIndex = Model->Nodes[NodeGroup->Nodes[0]].iSurf;
// fill out the NodeGroup/mapping, as UModelComponent::GetStaticLightingInfo did
SomeModelComponent->GetSurfaceLightMapResolution(SurfaceIndex, true, NodeGroup->SizeX, NodeGroup->SizeY, NodeGroup->WorldToMap, &NodeGroup->Nodes);
// Make sure mapping will have valid size
NodeGroup->SizeX = FMath::Max(NodeGroup->SizeX, 1);
NodeGroup->SizeY = FMath::Max(NodeGroup->SizeY, 1);
NodeGroup->MapToWorld = NodeGroup->WorldToMap.InverseFast();
// Cache the surface's vertices and triangles.
NodeGroup->BoundingBox.Init();
TArray<int32> ComponentVisibilityIds;
for(int32 NodeIndex = 0;NodeIndex < NodeGroup->Nodes.Num();NodeIndex++)
{
const FBspNode& Node = Model->Nodes[NodeGroup->Nodes[NodeIndex]];
const FBspSurf& NodeSurf = Model->Surfs[Node.iSurf];
const FVector& TextureBase = Model->Points[NodeSurf.pBase];
const FVector& TextureX = Model->Vectors[NodeSurf.vTextureU];
const FVector& TextureY = Model->Vectors[NodeSurf.vTextureV];
const int32 BaseVertexIndex = NodeGroup->Vertices.Num();
// Compute the surface's tangent basis.
FVector NodeTangentX = Model->Vectors[NodeSurf.vTextureU].GetSafeNormal();
FVector NodeTangentY = Model->Vectors[NodeSurf.vTextureV].GetSafeNormal();
FVector NodeTangentZ = Model->Vectors[NodeSurf.vNormal].GetSafeNormal();
// Generate the node's vertices.
for(uint32 VertexIndex = 0;VertexIndex < Node.NumVertices;VertexIndex++)
{
const FVert& Vert = Model->Verts[Node.iVertPool + VertexIndex];
const FVector& VertexWorldPosition = Model->Points[Vert.pVertex];
FStaticLightingVertex* DestVertex = new(NodeGroup->Vertices) FStaticLightingVertex;
DestVertex->WorldPosition = VertexWorldPosition;
DestVertex->TextureCoordinates[0].X = ((VertexWorldPosition - TextureBase) | TextureX) / UModel::GetGlobalBSPTexelScale();
DestVertex->TextureCoordinates[0].Y = ((VertexWorldPosition - TextureBase) | TextureY) / UModel::GetGlobalBSPTexelScale();
DestVertex->TextureCoordinates[1].X = NodeGroup->WorldToMap.TransformPosition(VertexWorldPosition).X;
DestVertex->TextureCoordinates[1].Y = NodeGroup->WorldToMap.TransformPosition(VertexWorldPosition).Y;
DestVertex->WorldTangentX = NodeTangentX;
DestVertex->WorldTangentY = NodeTangentY;
DestVertex->WorldTangentZ = NodeTangentZ;
// Include the vertex in the surface's bounding box.
NodeGroup->BoundingBox += VertexWorldPosition;
}
// Generate the node's vertex indices.
for(uint32 VertexIndex = 2;VertexIndex < Node.NumVertices;VertexIndex++)
{
NodeGroup->TriangleVertexIndices.Add(BaseVertexIndex + 0);
NodeGroup->TriangleVertexIndices.Add(BaseVertexIndex + VertexIndex);
NodeGroup->TriangleVertexIndices.Add(BaseVertexIndex + VertexIndex - 1);
// track the source surface for each triangle
NodeGroup->TriangleSurfaceMap.Add(Node.iSurf);
}
UModelComponent* Component = Level->ModelComponents[Node.ComponentIndex];
if (Component->VisibilityId == INDEX_NONE)
{
if (World->GetWorldSettings()->bPrecomputeVisibility)
{
// Make sure the level gets dirtied since we are changing the visibility Id of a component in it
bMarkLevelDirty = true;
}
Component->VisibilityId = NextVisibilityId;
NextVisibilityId++;
}
ComponentVisibilityIds.AddUnique(Component->VisibilityId);
}
// Continue only if the component accepts lights (all components in a node group have the same value)
// TODO: If we expose CastShadow for BSP in the future, reenable this condition and make sure
// node grouping logic is updated to account for CastShadow as well
//if (SomeModelComponent->bAcceptsLights || SomeModelComponent->CastShadow)
{
// Create the object to represent the surface's mapping/mesh to the static lighting system,
// the model is now the owner, and all nodes have the same
FBSPSurfaceStaticLighting* SurfaceStaticLighting = new FBSPSurfaceStaticLighting(NodeGroup, Model, SomeModelComponent);
// Give the surface mapping the visibility Id's of all components that have nodes in it
// This results in fairly ineffective precomputed visibility with BSP but is necessary since BSP mappings contain geometry from multiple components
SurfaceStaticLighting->VisibilityIds = ComponentVisibilityIds;
Meshes.Add(SurfaceStaticLighting);
LightingMeshBounds += SurfaceStaticLighting->BoundingBox;
if (SomeModelComponent->CastShadow)
{
UpdateAutomaticImportanceVolumeBounds( SurfaceStaticLighting->BoundingBox );
}
FStaticLightingMapping* CurrentMapping = SurfaceStaticLighting;
if (GLightmassDebugOptions.bSortMappings)
{
int32 InsertIndex = UnSortedMappings.AddZeroed();
FStaticLightingMappingSortHelper& Helper = UnSortedMappings[InsertIndex];
Helper.Mapping = CurrentMapping;
Helper.NumTexels = CurrentMapping->GetTexelCount();
}
else
{
Mappings.Add(CurrentMapping);
if (bBuildLightingForBSP)
{
CurrentMapping->Mesh->Guid = FGuid(0,0,0,DeterministicIndex++);
}
}
if (bBuildLightingForBSP)
{
CurrentMapping->bProcessMapping = true;
}
// count how many node groups have yet to come back as complete
Model->NumIncompleteNodeGroups++;
// add this mapping to the list of mappings to be applied later
Model->CachedMappings.Add(SurfaceStaticLighting);
}
}
}
if (bMarkLevelDirty)
{
Level->MarkPackageDirty();
}
}
/**
* Generates mappings/meshes for the given NodeGroups
*
* @param Level Level to build BSP lighting info for
* @param NodeGroupsToBuild The node groups to build the BSP lighting info for
*/
void FStaticLightingSystem::AddBSPStaticLightingInfo(ULevel* Level, TArray<FNodeGroup*>& NodeGroupsToBuild)
{
// For BSP, we aren't Component-centric, so we can't use the GetStaticLightingInfo
// function effectively. Instead, we look across all nodes in the Level's model and
// generate NodeGroups - which are groups of nodes that are coplanar, adjacent, and
// have the same lightmap resolution (henceforth known as being "conodes"). Each
// NodeGroup will get a mapping created for it
// cache the model
UModel* Model = Level->Model;
// reset the number of incomplete groups
Model->NumIncompleteNodeGroups = 0;
Model->CachedMappings.Empty();
Model->bInvalidForStaticLighting = false;
// now we need to make the mappings/meshes
for (int32 NodeGroupIdx = 0; NodeGroupIdx < NodeGroupsToBuild.Num(); NodeGroupIdx++)
{
FNodeGroup* NodeGroup = NodeGroupsToBuild[NodeGroupIdx];
if (NodeGroup && NodeGroup->Nodes.Num())
{
// get one of the surfaces/components from the NodeGroup
// @todo UE4: Remove need for GetSurfaceLightMapResolution to take a surfaceindex, or a ModelComponent :)
UModelComponent* SomeModelComponent = Level->ModelComponents[Model->Nodes[NodeGroup->Nodes[0]].ComponentIndex];
int32 SurfaceIndex = Model->Nodes[NodeGroup->Nodes[0]].iSurf;
// fill out the NodeGroup/mapping, as UModelComponent::GetStaticLightingInfo did
SomeModelComponent->GetSurfaceLightMapResolution(SurfaceIndex, true, NodeGroup->SizeX, NodeGroup->SizeY, NodeGroup->WorldToMap, &NodeGroup->Nodes);
NodeGroup->MapToWorld = NodeGroup->WorldToMap.InverseFast();
// Cache the surface's vertices and triangles.
NodeGroup->BoundingBox.Init();
for(int32 NodeIndex = 0;NodeIndex < NodeGroup->Nodes.Num();NodeIndex++)
{
const FBspNode& Node = Model->Nodes[NodeGroup->Nodes[NodeIndex]];
const FBspSurf& NodeSurf = Model->Surfs[Node.iSurf];
const FVector& TextureBase = Model->Points[NodeSurf.pBase];
const FVector& TextureX = Model->Vectors[NodeSurf.vTextureU];
const FVector& TextureY = Model->Vectors[NodeSurf.vTextureV];
const int32 BaseVertexIndex = NodeGroup->Vertices.Num();
// Compute the surface's tangent basis.
FVector NodeTangentX = Model->Vectors[NodeSurf.vTextureU].GetSafeNormal();
FVector NodeTangentY = Model->Vectors[NodeSurf.vTextureV].GetSafeNormal();
FVector NodeTangentZ = Model->Vectors[NodeSurf.vNormal].GetSafeNormal();
// Generate the node's vertices.
for(uint32 VertexIndex = 0;VertexIndex < Node.NumVertices;VertexIndex++)
{
const FVert& Vert = Model->Verts[Node.iVertPool + VertexIndex];
const FVector& VertexWorldPosition = Model->Points[Vert.pVertex];
FStaticLightingVertex* DestVertex = new(NodeGroup->Vertices) FStaticLightingVertex;
DestVertex->WorldPosition = VertexWorldPosition;
DestVertex->TextureCoordinates[0].X = ((VertexWorldPosition - TextureBase) | TextureX) / UModel::GetGlobalBSPTexelScale();
DestVertex->TextureCoordinates[0].Y = ((VertexWorldPosition - TextureBase) | TextureY) / UModel::GetGlobalBSPTexelScale();
DestVertex->TextureCoordinates[1].X = NodeGroup->WorldToMap.TransformPosition(VertexWorldPosition).X;
DestVertex->TextureCoordinates[1].Y = NodeGroup->WorldToMap.TransformPosition(VertexWorldPosition).Y;
DestVertex->WorldTangentX = NodeTangentX;
DestVertex->WorldTangentY = NodeTangentY;
DestVertex->WorldTangentZ = NodeTangentZ;
// Include the vertex in the surface's bounding box.
NodeGroup->BoundingBox += VertexWorldPosition;
}
// Generate the node's vertex indices.
for(uint32 VertexIndex = 2;VertexIndex < Node.NumVertices;VertexIndex++)
{
NodeGroup->TriangleVertexIndices.Add(BaseVertexIndex + 0);
NodeGroup->TriangleVertexIndices.Add(BaseVertexIndex + VertexIndex);
NodeGroup->TriangleVertexIndices.Add(BaseVertexIndex + VertexIndex - 1);
// track the source surface for each triangle
NodeGroup->TriangleSurfaceMap.Add(Node.iSurf);
}
}
// Continue only if the component accepts lights (all components in a node group have the same value)
// TODO: If we expose CastShadow for BSP in the future, reenable this condition and make sure
// node grouping logic is updated to account for CastShadow as well
//if (SomeModelComponent->bAcceptsLights || SomeModelComponent->CastShadow)
{
// Create the object to represent the surface's mapping/mesh to the static lighting system,
// the model is now the owner, and all nodes have the same
FBSPSurfaceStaticLighting* SurfaceStaticLighting = new FBSPSurfaceStaticLighting(NodeGroup, Model, SomeModelComponent);
Meshes.Add(SurfaceStaticLighting);
LightingMeshBounds += SurfaceStaticLighting->BoundingBox;
if (SomeModelComponent->CastShadow)
{
UpdateAutomaticImportanceVolumeBounds( SurfaceStaticLighting->BoundingBox );
}
FStaticLightingMapping* CurrentMapping = SurfaceStaticLighting;
if (GLightmassDebugOptions.bSortMappings)
{
int32 InsertIndex = UnSortedMappings.AddZeroed();
FStaticLightingMappingSortHelper& Helper = UnSortedMappings[InsertIndex];
Helper.Mapping = CurrentMapping;
Helper.NumTexels = CurrentMapping->GetTexelCount();
}
else
{
Mappings.Add(CurrentMapping);
CurrentMapping->Mesh->Guid = FGuid(0,0,0,DeterministicIndex++);
}
CurrentMapping->bProcessMapping = true;
// count how many node groups have yet to come back as complete
Model->NumIncompleteNodeGroups++;
// add this mapping to the list of mappings to be applied later
Model->CachedMappings.Add(SurfaceStaticLighting);
}
}
}
}
void FStaticLightingSystem::AddPrimitiveStaticLightingInfo(FStaticLightingPrimitiveInfo& PrimitiveInfo, bool bBuildActorLighting)
{
// Verify a one to one relationship between mappings and meshes
//@todo - merge FStaticLightingMesh and FStaticLightingMapping
check(PrimitiveInfo.Meshes.Num() == PrimitiveInfo.Mappings.Num());
// Add the component's shadow casting meshes to the system.
for(int32 MeshIndex = 0;MeshIndex < PrimitiveInfo.Meshes.Num();MeshIndex++)
{
FStaticLightingMesh* Mesh = PrimitiveInfo.Meshes[MeshIndex];
if (Mesh)
{
Mesh->VisibilityIds.Add(PrimitiveInfo.VisibilityId);
if (!GLightmassDebugOptions.bSortMappings && bBuildActorLighting)
{
Mesh->Guid = FGuid(0, 0, 0, DeterministicIndex++);
}
Meshes.Add(Mesh);
LightingMeshBounds += Mesh->BoundingBox;
if (Mesh->bCastShadow)
{
UpdateAutomaticImportanceVolumeBounds(Mesh->BoundingBox);
}
}
}
// If lighting is being built for this component, add its mappings to the system.
for(int32 MappingIndex = 0;MappingIndex < PrimitiveInfo.Mappings.Num();MappingIndex++)
{
FStaticLightingMapping* CurrentMapping = PrimitiveInfo.Mappings[MappingIndex];
if (GbLogAddingMappings)
{
FStaticLightingMesh* SLMesh = CurrentMapping->Mesh;
if (SLMesh)
{
//UE_LOG(LogStaticLightingSystem, Log, TEXT("Adding %32s: 0x%08p - %s"), *(CurrentMapping->GetDescription()), (PTRINT)(SLMesh->Component), *(SLMesh->Guid.ToString()));
}
else
{
//UE_LOG(LogStaticLightingSystem, Log, TEXT("Adding %32s: 0x%08x - %s"), *(CurrentMapping->GetDescription()), 0, TEXT("NO MESH????"));
}
}
if (bBuildActorLighting)
{
CurrentMapping->bProcessMapping = true;
}
if (GLightmassDebugOptions.bSortMappings)
{
int32 InsertIndex = UnSortedMappings.AddZeroed();
FStaticLightingMappingSortHelper& Helper = UnSortedMappings[InsertIndex];
Helper.Mapping = CurrentMapping;
Helper.NumTexels = Helper.Mapping->GetTexelCount();
}
else
{
Mappings.Add(CurrentMapping);
}
}
}
bool FStaticLightingSystem::CreateLightmassProcessor()
{
FLightmassStatistics::FScopedGather SwarmStartStatScope(LightmassProcessStatistics.SwarmStartupTime);
GWarn->StatusForceUpdate( -1, -1, LOCTEXT("StartingSwarmConnectionStatus", "Starting up Swarm Connection...") );
if (Options.bOnlyBuildVisibility && !World->GetWorldSettings()->bPrecomputeVisibility)
{
FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "BuildFailed_VisibilityOnlyButVisibilityDisabled", "'Build Only Visibility' option was enabled but precomputed visibility is disabled! Aborting build."));
return false;
}
NSwarm::FSwarmInterface::Initialize(*(FString(FPlatformProcess::BaseDir()) + TEXT("..\\DotNET\\SwarmInterface.dll")));
// Create the processor
check(LightmassProcessor == NULL);
LightmassProcessor = new FLightmassProcessor(*this, Options.bDumpBinaryResults, Options.bOnlyBuildVisibility);
check(LightmassProcessor);
if (LightmassProcessor->IsSwarmConnectionIsValid() == false)
{
UE_LOG(LogStaticLightingSystem, Warning, TEXT("Failed to connect to Swarm."));
FMessageDialog::Open( EAppMsgType::Ok,
#if USE_LOCAL_SWARM_INTERFACE
LOCTEXT("FailedToConnectToSwarmDialogMessage_CheckNetwork", "Failed to connect to Swarm. Check that your network interface supports multicast.")
#else
LOCTEXT("FailedToConnectToSwarmDialogMessage", "Failed to connect to Swarm.")
#endif
);
delete LightmassProcessor;
LightmassProcessor = NULL;
return false;
}
return true;
}
void FStaticLightingSystem::GatherScene()
{
LightmassProcessStatistics = FLightmassStatistics();
GWarn->StatusUpdate( 0, Meshes.Num() + Mappings.Num(), LOCTEXT("GatherSceneStatusMessage", "Collecting the scene...") );
FLightmassStatistics::FScopedGather SceneStatScope(LightmassProcessStatistics.CollectLightmassSceneTime);
// Grab the exporter and fill in the meshes
//@todo. This should be exported to the 'processor' as it will be used on the input side as well...
FLightmassExporter* LightmassExporter = LightmassProcessor->GetLightmassExporter();
check(LightmassExporter);
// The Level settings...
AWorldSettings* WorldSettings = World->GetWorldSettings();
if (WorldSettings)
{
LightmassExporter->SetLevelSettings(WorldSettings->LightmassSettings);
}
else
{
FLightmassWorldInfoSettings TempSettings;
LightmassExporter->SetLevelSettings(TempSettings);
}
LightmassExporter->SetNumUnusedLocalCores(Options.NumUnusedLocalCores);
LightmassExporter->SetQualityLevel(Options.QualityLevel);
if (World->PersistentLevel && Options.ShouldBuildLightingForLevel( World->PersistentLevel ))
{
LightmassExporter->SetLevelName(World->PersistentLevel->GetPathName());
}
LightmassExporter->ClearImportanceVolumes();
for( TObjectIterator<ALightmassImportanceVolume> It ; It ; ++It )
{
ALightmassImportanceVolume* LMIVolume = *It;
if (World->ContainsActor(LMIVolume) && !LMIVolume->IsPendingKill() && ShouldOperateOnLevel(LMIVolume->GetLevel()))
{
LightmassExporter->AddImportanceVolume(LMIVolume);
}
}
for( TObjectIterator<ALightmassCharacterIndirectDetailVolume> It ; It ; ++It )
{
ALightmassCharacterIndirectDetailVolume* LMDetailVolume = *It;
if (World->ContainsActor(LMDetailVolume) && !LMDetailVolume->IsPendingKill() && ShouldOperateOnLevel(LMDetailVolume->GetLevel()))
{
LightmassExporter->AddCharacterIndirectDetailVolume(LMDetailVolume);
}
}
for (TObjectIterator<AVolumetricLightmapDensityVolume> It; It; ++It)
{
AVolumetricLightmapDensityVolume* DetailVolume = *It;
if (World->ContainsActor(DetailVolume) && !DetailVolume->IsPendingKill() && ShouldOperateOnLevel(DetailVolume->GetLevel()))
{
LightmassExporter->VolumetricLightmapDensityVolumes.Add(DetailVolume);
}
}
for( TObjectIterator<ULightmassPortalComponent> It ; It ; ++It )
{
ULightmassPortalComponent* LMPortal = *It;
if (LMPortal->GetOwner() && World->ContainsActor(LMPortal->GetOwner()) && !LMPortal->IsPendingKill() && ShouldOperateOnLevel(LMPortal->GetOwner()->GetLevel()))
{
LightmassExporter->AddPortal(LMPortal);
}
}
float MinimumImportanceVolumeExtentWithoutWarning = 0.0f;
verify(GConfig->GetFloat(TEXT("DevOptions.StaticLightingSceneConstants"), TEXT("MinimumImportanceVolumeExtentWithoutWarning"), MinimumImportanceVolumeExtentWithoutWarning, GLightmassIni));
// If we have no importance volumes, then we'll synthesize one now. A scene without any importance volumes will not yield
// expected lighting results, so it's important to have a volume to pass to Lightmass.
if (LightmassExporter->GetImportanceVolumes().Num() == 0)
{
FBox ReasonableSceneBounds = AutomaticImportanceVolumeBounds;
if (ReasonableSceneBounds.GetExtent().SizeSquared() > (MinimumImportanceVolumeExtentWithoutWarning * MinimumImportanceVolumeExtentWithoutWarning))
{
// Emit a serious warning to the user about performance.
FMessageLog("LightingResults").PerformanceWarning(LOCTEXT("LightmassError_MissingImportanceVolume", "No importance volume found and the scene is so large that the automatically synthesized volume will not yield good results. Please add a tightly bounding lightmass importance volume to optimize your scene's quality and lighting build times."));
// Clamp the size of the importance volume we create to a reasonable size
ReasonableSceneBounds = FBox(ReasonableSceneBounds.GetCenter() - MinimumImportanceVolumeExtentWithoutWarning, ReasonableSceneBounds.GetCenter() + MinimumImportanceVolumeExtentWithoutWarning);
}
else
{
// The scene isn't too big, so we'll use the scene's bounds as a synthetic importance volume
// NOTE: We don't want to pop up a message log for this common case when creating a new level, so we just spray a log message. It's not very important to a user.
UE_LOG(LogStaticLightingSystem, Warning, TEXT("No importance volume found, so the scene bounding box was used. You can optimize your scene's quality and lighting build times by adding importance volumes."));
float AutomaticImportanceVolumeExpandBy = 0.0f;
verify(GConfig->GetFloat(TEXT("DevOptions.StaticLightingSceneConstants"), TEXT("AutomaticImportanceVolumeExpandBy"), AutomaticImportanceVolumeExpandBy, GLightmassIni));
// Expand the scene's bounds a bit to make sure volume lighting samples placed on surfaces are inside
ReasonableSceneBounds = ReasonableSceneBounds.ExpandBy(AutomaticImportanceVolumeExpandBy);
}
LightmassExporter->AddImportanceVolumeBoundingBox(ReasonableSceneBounds);
}
const int32 NumMeshesAndMappings = Meshes.Num() + Mappings.Num();
const int32 ProgressUpdateFrequency = FMath::Max<int32>(NumMeshesAndMappings / 20, 1);
// Meshes
for( int32 MeshIdx=0; !GEditor->GetMapBuildCancelled() && MeshIdx < Meshes.Num(); MeshIdx++ )
{
Meshes[MeshIdx]->ExportMeshInstance(LightmassExporter);
if (MeshIdx % ProgressUpdateFrequency == 0)
{
GWarn->UpdateProgress( MeshIdx, NumMeshesAndMappings );
}
}
// Mappings
for( int32 MappingIdx=0; !GEditor->GetMapBuildCancelled() && MappingIdx < Mappings.Num(); MappingIdx++ )
{
Mappings[MappingIdx]->ExportMapping(LightmassExporter);
if (MappingIdx % ProgressUpdateFrequency == 0)
{
GWarn->UpdateProgress( Meshes.Num() + MappingIdx, NumMeshesAndMappings );
}
}
for (int32 LightIndex = 0; LightIndex < Lights.Num(); LightIndex++)
{
ULightComponentBase* LightBase = Lights[LightIndex];
USkyLightComponent* SkyLight = Cast<USkyLightComponent>(LightBase);
if (SkyLight && (SkyLight->Mobility == EComponentMobility::Static || SkyLight->Mobility == EComponentMobility::Stationary))
{
LightmassExporter->AddLight(SkyLight);
}
}
}
bool FStaticLightingSystem::InitiateLightmassProcessor()
{
// Run!
bool bSuccessful = false;
bool bOpenJobSuccessful = false;
if ( !GEditor->GetMapBuildCancelled() )
{
UE_LOG(LogStaticLightingSystem, Log, TEXT("Running Lightmass w/ ImmediateImport mode %s"), GLightmassDebugOptions.bUseImmediateImport ? TEXT("ENABLED") : TEXT("DISABLED"));
LightmassProcessor->SetImportCompletedMappingsImmediately(GLightmassDebugOptions.bUseImmediateImport);
UE_LOG(LogStaticLightingSystem, Log, TEXT("Running Lightmass w/ ImmediateProcess mode %s"), GLightmassDebugOptions.bImmediateProcessMappings ? TEXT("ENABLED") : TEXT("DISABLED"));
UE_LOG(LogStaticLightingSystem, Log, TEXT("Running Lightmass w/ Sorting mode %s"), GLightmassDebugOptions.bSortMappings ? TEXT("ENABLED") : TEXT("DISABLED"));
UE_LOG(LogStaticLightingSystem, Log, TEXT("Running Lightmass w/ Mapping paddings %s"), GLightmassDebugOptions.bPadMappings ? TEXT("ENABLED") : TEXT("DISABLED"));
UE_LOG(LogStaticLightingSystem, Log, TEXT("Running Lightmass w/ Mapping debug paddings %s"), GLightmassDebugOptions.bDebugPaddings ? TEXT("ENABLED") : TEXT("DISABLED"));
{
FLightmassStatistics::FScopedGather OpenJobStatScope(LightmassProcessStatistics.SwarmJobOpenTime);
bOpenJobSuccessful = LightmassProcessor->OpenJob();
}
if (bOpenJobSuccessful)
{
LightmassProcessor->InitiateExport();
bSuccessful = true;
CurrentBuildStage = FStaticLightingSystem::AmortizedExport;
}
}
return bSuccessful;
}
void FStaticLightingSystem::KickoffSwarm()
{
bool bSuccessful = LightmassProcessor->BeginRun();
if (bSuccessful)
{
CurrentBuildStage = FStaticLightingSystem::AsynchronousBuilding;
}
else
{
FStaticLightingManager::Get()->FailLightingBuild(LOCTEXT("SwarmKickoffFailedMessage", "Lighting build failed. Swarm failed to kick off. Compile Unreal Lightmass."));
}
}
bool FStaticLightingSystem::FinishLightmassProcess()
{
bool bSuccessful = false;
GEditor->ResetTransaction( LOCTEXT("KeepLightingTransReset", "Applying Lighting") );
CurrentBuildStage = FStaticLightingSystem::Import;
double TimeWaitingOnUserToAccept = FPlatformTime::Seconds() - WaitForUserAcceptStartTime;
{
FScopedSlowTask SlowTask(7);
SlowTask.MakeDialog();
SlowTask.EnterProgressFrame(1, LOCTEXT("InvalidatingPreviousLightingStatus", "Invalidating previous lighting"));
InvalidateStaticLighting();
SlowTask.EnterProgressFrame(1, LOCTEXT("ImportingBuiltStaticLightingStatus", "Importing built static lighting"));
bSuccessful = LightmassProcessor->CompleteRun();
SlowTask.EnterProgressFrame();
if (bSuccessful)
{
CompleteDeterministicMappings(LightmassProcessor);
if (!Options.bOnlyBuildVisibility)
{
FLightmassStatistics::FScopedGather FinishStatScope(LightmassStatistics.FinishingTime);
ULightComponent::ReassignStationaryLightChannels(GWorld, true, LightingScenario);
}
}
SlowTask.EnterProgressFrame(1, LOCTEXT("EncodingTexturesStaticLightingStatis", "Encoding textures"));
EncodeTextures(bSuccessful);
SlowTask.EnterProgressFrame();
{
FLightmassStatistics::FScopedGather CloseJobStatScope(LightmassProcessStatistics.SwarmJobCloseTime);
bSuccessful = LightmassProcessor->CloseJob() && bSuccessful;
}
{
FLightmassStatistics::FScopedGather FinishStatScope(LightmassStatistics.FinishingTime);
// Add in the time measurements from the LightmassProcessor
LightmassStatistics += LightmassProcessor->GetStatistics();
// A final update on the lighting build warnings and errors dialog, now that everything is finished
FMessageLog("LightingResults").Open();
// Check the for build cancellation.
bBuildCanceled = bBuildCanceled || GEditor->GetMapBuildCancelled();
bSuccessful = bSuccessful && !bBuildCanceled;
FStatsViewerModule& StatsViewerModule = FModuleManager::Get().LoadModuleChecked<FStatsViewerModule>(TEXT("StatsViewer"));
if (bSuccessful)
{
StatsViewerModule.GetPage(EStatsPage::LightingBuildInfo)->Refresh();
}
bool bShowLightingBuildInfo = false;
GConfig->GetBool( TEXT("LightingBuildOptions"), TEXT("ShowLightingBuildInfo"), bShowLightingBuildInfo, GEditorPerProjectIni );
if( bShowLightingBuildInfo )
{
StatsViewerModule.GetPage(EStatsPage::LightingBuildInfo)->Show();
}
}
SlowTask.EnterProgressFrame();
ApplyNewLightingData(bSuccessful);
SlowTask.EnterProgressFrame();
// Finish up timing statistics
LightmassStatistics += LightmassProcessStatistics;
LightmassStatistics.TotalTime += FPlatformTime::Seconds() - StartTime - TimeWaitingOnUserToAccept;
}
ReportStatistics();
return bSuccessful;
}
void FStaticLightingSystem::UpdateLightingBuild()
{
if (CurrentBuildStage == FStaticLightingSystem::AmortizedExport)
{
bool bCompleted = LightmassProcessor->ExecuteAmortizedMaterialExport();
FFormatNamedArguments Args;
Args.Add( TEXT("PercentDone"), FText::AsPercent( LightmassProcessor->GetAmortizedExportPercentDone() ) );
FText Text = FText::Format( LOCTEXT("LightExportProgressMessage", "Exporting lighting data: {PercentDone} Done"), Args );
FStaticLightingManager::Get()->SetNotificationText( Text );
if (bCompleted)
{
CurrentBuildStage = FStaticLightingSystem::SwarmKickoff;
}
}
else if (CurrentBuildStage == FStaticLightingSystem::SwarmKickoff)
{
FText Text = LOCTEXT("LightKickoffSwarmMessage", "Kicking off Swarm");
FStaticLightingManager::Get()->SetNotificationText( Text );
KickoffSwarm();
}
else if (CurrentBuildStage == FStaticLightingSystem::AsynchronousBuilding)
{
bool bFinished = LightmassProcessor->Update();
FString ScenarioString;
if (LightingScenario)
{
FString PackageName = FPackageName::GetShortName(LightingScenario->GetOutermost()->GetName());
ScenarioString = FString(TEXT(" for ")) + PackageName;
}
FText Text = FText::Format(LOCTEXT("LightBuildProgressMessage", "Building lighting{0}: {1}%"), FText::FromString(ScenarioString), FText::AsNumber(LightmassProcessor->GetAsyncPercentDone()));
FStaticLightingManager::Get()->SetNotificationText( Text );
if (bFinished)
{
LightmassStatistics.ProcessingTime += FPlatformTime::Seconds() - ProcessingStartTime;
WaitForUserAcceptStartTime = FPlatformTime::Seconds();
FStaticLightingManager::Get()->ClearCurrentNotification();
if (LightmassProcessor->IsProcessingCompletedSuccessfully())
{
CurrentBuildStage = FStaticLightingSystem::AutoApplyingImport;
}
else
{
// automatically fail lighting build (discard)
FStaticLightingManager::Get()->FailLightingBuild();
CurrentBuildStage = FStaticLightingSystem::Finished;
}
}
}
else if ( CurrentBuildStage == FStaticLightingSystem::AutoApplyingImport )
{
if ( CanAutoApplyLighting() || IsRunningCommandlet() )
{
bool bAutoApplyFailed = false;
FStaticLightingManager::Get()->SendBuildDoneNotification(bAutoApplyFailed);
FStaticLightingManager::ProcessLightingData();
CurrentBuildStage = FStaticLightingSystem::Finished;
}
else
{
bool bAutoApplyFailed = true;
FStaticLightingManager::Get()->SendBuildDoneNotification(bAutoApplyFailed);
CurrentBuildStage = FStaticLightingSystem::WaitingForImport;
}
}
else if (CurrentBuildStage == FStaticLightingSystem::ImportRequested)
{
FStaticLightingManager::ProcessLightingData();
CurrentBuildStage = FStaticLightingSystem::Finished;
}
}
void FStaticLightingSystem::UpdateAutomaticImportanceVolumeBounds( const FBox& MeshBounds )
{
// Note: skyboxes will be excluded if they are properly setup to not cast shadows
AutomaticImportanceVolumeBounds += MeshBounds;
}
void FStaticLightingSystem::GatherBuildDataResourcesToKeep(const ULevel* InLevel)
{
// This is only required is using a lighting scenario, otherwise the build data is saved within the level itself and follows it's inclusion in the lighting build.
if (InLevel && LightingScenario)
{
BuildDataResourcesToKeep.Add(InLevel->LevelBuildDataId);
for (const UModelComponent * ModelComponent : InLevel->ModelComponents)
{
if (!ModelComponent) // Skip null models
{
continue;
}
ModelComponent->AddMapBuildDataGUIDs(BuildDataResourcesToKeep);
}
for (const AActor* Actor : InLevel->Actors)
{
if (!Actor) // Skip null actors
{
continue;
}
for (const UActorComponent* Component : Actor->GetComponents())
{
if (!Component) // Skip null components
{
continue;
}
const UPrimitiveComponent* PrimitiveComponent = Cast<UPrimitiveComponent>(Component);
if (PrimitiveComponent)
{
PrimitiveComponent->AddMapBuildDataGUIDs(BuildDataResourcesToKeep);
continue;
}
const ULightComponent* LightComponent = Cast<ULightComponent>(Component);
if (LightComponent)
{
BuildDataResourcesToKeep.Add(LightComponent->LightGuid);
continue;
}
const UReflectionCaptureComponent* ReflectionCaptureComponent = Cast<UReflectionCaptureComponent>(Component);
if (ReflectionCaptureComponent)
{
BuildDataResourcesToKeep.Add(ReflectionCaptureComponent->MapBuildDataId);
continue;
}
}
}
}
}
bool FStaticLightingSystem::CanAutoApplyLighting() const
{
const bool bAutoApplyEnabled = GetDefault<ULevelEditorMiscSettings>()->bAutoApplyLightingEnable;
const bool bSlowTask = GIsSlowTask;
const bool bInterpEditMode = GLevelEditorModeTools().IsModeActive( FBuiltinEditorModes::EM_InterpEdit );
const bool bPlayWorldValid = GEditor->PlayWorld != nullptr;
const bool bAnyMenusVisible = (FSlateApplication::IsInitialized() && FSlateApplication::Get().AnyMenusVisible());
//const bool bIsInteratcting = false;// FSlateApplication::Get().GetMouseCaptor().IsValid() || GEditor->IsUserInteracting();
const bool bHasGameOrProjectLoaded = FApp::HasProjectName();
return ( bAutoApplyEnabled && !bSlowTask && !bInterpEditMode && !bPlayWorldValid && !bAnyMenusVisible/* && !bIsInteratcting */&& !GIsDemoMode && bHasGameOrProjectLoaded );
}
/**
* Clear out all the binary dump log files, so the next run will have just the needed files for rendering
*/
void FStaticLightingSystem::ClearBinaryDumps()
{
IFileManager::Get().DeleteDirectory(*FString::Printf(TEXT("%sLogs/Lighting_%s"), *FPaths::ProjectDir(), TEXT("Lightmass")), false, true);
}
/** Marks all lights used in the calculated lightmap as used in a lightmap, and calls Apply on the texture mapping. */
void FStaticLightingSystem::ApplyMapping(
FStaticLightingTextureMapping* TextureMapping,
FQuantizedLightmapData* QuantizedData,
const TMap<ULightComponent*,FShadowMapData2D*>& ShadowMapData) const
{
TextureMapping->Apply(QuantizedData, ShadowMapData, LightingScenario);
}
UWorld* FStaticLightingSystem::GetWorld() const
{
return World;
}
bool FStaticLightingSystem::IsAsyncBuilding() const
{
return CurrentBuildStage == FStaticLightingSystem::AsynchronousBuilding;
}
bool FStaticLightingSystem::IsAmortizedExporting() const
{
return CurrentBuildStage == FStaticLightingSystem::AmortizedExport;
}
void UEditorEngine::BuildLighting(const FLightingBuildOptions& Options)
{
// Forcibly shut down all texture property windows as they become invalid during a light build
FAssetEditorManager& AssetEditorManager = FAssetEditorManager::Get();
TArray<UObject*> EditedAssets = AssetEditorManager.GetAllEditedAssets();
for (int32 AssetIdx = 0; AssetIdx < EditedAssets.Num(); AssetIdx++)
{
UObject* EditedAsset = EditedAssets[AssetIdx];
if (EditedAsset->IsA(UTexture2D::StaticClass()))
{
IAssetEditorInstance* Editor = AssetEditorManager.FindEditorForAsset(EditedAsset, false);
if (Editor)
{
Editor->CloseWindow();
}
}
}
FEditorDelegates::OnLightingBuildStarted.Broadcast();
FStaticLightingManager::Get()->CreateStaticLightingSystem(Options);
}
void UEditorEngine::UpdateBuildLighting()
{
FStaticLightingManager::Get()->UpdateBuildLighting();
}
bool UEditorEngine::IsLightingBuildCurrentlyRunning() const
{
return FStaticLightingManager::Get()->IsLightingBuildCurrentlyRunning();
}
bool UEditorEngine::IsLightingBuildCurrentlyExporting() const
{
return FStaticLightingManager::Get()->IsLightingBuildCurrentlyExporting();
}
bool UEditorEngine::WarnIfLightingBuildIsCurrentlyRunning()
{
bool bFailure = IsLightingBuildCurrentlyRunning();
if (bFailure)
{
FNotificationInfo Info( LOCTEXT("LightBuildUnderwayWarning", "Static light is currently building! Please cancel it to proceed!") );
Info.ExpireDuration = 5.0f;
TSharedPtr<SNotificationItem> Notification = FSlateNotificationManager::Get().AddNotification(Info);
if (Notification.IsValid())
{
Notification->SetCompletionState(SNotificationItem::CS_Fail);
}
}
else if (FEditorBuildUtils::IsBuildCurrentlyRunning())
{
// Another, non-lighting editor build is running.
FNotificationInfo Info( LOCTEXT("EditorBuildUnderwayWarning", "A build process is currently underway! Please cancel it to proceed!") );
Info.ExpireDuration = 5.0f;
TSharedPtr<SNotificationItem> Notification = FSlateNotificationManager::Get().AddNotification(Info);
if (Notification.IsValid())
{
Notification->SetCompletionState(SNotificationItem::CS_Fail);
}
bFailure = true;
}
return bFailure;
}
#undef LOCTEXT_NAMESPACE