Files
UnrealEngineUWP/Engine/Source/Editor/UnrealEd/Private/StaticLightingSystem/StaticLightingSystem.cpp
Matt Kuhlenschmidt cd967bc733 Copying //UE4/Dev-Editor to //UE4/Dev-Main (Source: //UE4/Dev-Editor @ 3807299)
#lockdown Nick.Penwarden
#rb none

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

Change 3528776 by Yannick.Lange

	Allow thumbnails to be captured from a viewport always.

	#jira UE-45392

Change 3564359 by Yannick.Lange

	Back out part of changelist 3528776: Revert allowing thumbnails to be captured from a viewport to fix UE-47827 & UE-47785.

	#jira UE-47785,  UE-47827

Change 3740671 by Matt.Kuhlenschmidt

	Make the font editor canvas respect dpi scale

Change 3740810 by Josh.Engebretson

	PR #4138: Log GenerateProjectFiles to file when using UVS (Contributed by projectgheist)
	#jira UE-51358

Change 3740939 by Cody.Albert

	Fixing some #ifdefs that should be #ifs

Change 3741089 by Alexis.Matte

	Make sure bImportBoneTracks is set to true when importing into level
	#jira UE-51999

Change 3741101 by Alexis.Matte

	Fix the import material search
	#jira UE-51857

Change 3741690 by Jamie.Dale

	Guard against an invalid index in SLocalizationCommandletExecutor::Tick

	#jira UE-52067

Change 3741710 by Jamie.Dale

	Made a static variable also const

Change 3741724 by Michael.Dupuis

	Added missing shader cache

Change 3742037 by Lauren.Ridge

	Details panels can now "own" a color picker so a different details panel refreshing doesn't close it. Also fixed refreshing state of the graph after changing texture or color parameter values

Change 3742250 by Matt.Kuhlenschmidt

	PR #4185: Output Log Filter tooltip shows %s instead of category. (Contributed by LordNed)


Change 3742308 by Lauren.Ridge

	Adding axis input to the material editor's viewport client

Change 3742380 by Matt.Kuhlenschmidt

	USD importer improvements

	- USD now stores source file information for reimports
	- Fixed bug where no geometry would be imported if an exporter had set a time code even without animated data
	- Prevent a crash if a mesh doesnt have enough valid data to be imported

Change 3742536 by Matt.Kuhlenschmidt

	Remove usd wrapper test project

Change 3743564 by Alexis.Matte

	Fix skeletal mesh screen size auto set value when importing a LOD

	#test none
	#jira UE-52100

Change 3743582 by Lauren.Ridge

	Fixing non-desktop compiles

Change 3743598 by Lauren.Ridge

	Fixing shadowed variable by renaming the global color picker weak ptr variable.

Change 3743636 by Lauren.Ridge

	Creating a new parameter immediately allows naming

Change 3743855 by Michael.Dupuis

	Added missing shader from cache

Change 3744277 by Lauren.Ridge

	Don't show empty Global group if it only contained the material layer param.

Change 3744330 by Lauren.Ridge

	Clarifying "no parameter" text

Change 3744529 by Lauren.Ridge

	Making Save to Child and Save To Sibling buttons work for material layer params, show on material layer param panel

Change 3744668 by Chris.Bunner

	Added shared layer input collection asset, a list of float/texture redirectors that allow setting globally in a material graph then retrieving within layer/blend graphs.
	Added output for number of unique shaders generated by a particular material instance.
	Show instruction counts when working on a material layer.
	Relaxed restrictions on material layers, base MA input is now optional.

	#jira UETOOL-1271, UETOOL-1278, UETOOL-1279

Change 3744669 by Chris.Bunner

	Added automated test for material layers, layer/blend instances and shared inputs.

Change 3744754 by Laz.Matech

	Adding localization data to QAGame for the Realtime Preview localization test.

	#jira none

Change 3744860 by Michael.Dupuis

	#jira UE-52049 : Do not update random stream in most case, only when adding new instances, or filling from built data

Change 3744965 by Chris.Bunner

	Rebuilt lighting in automated test map and updated reflection capture screenshot.

Change 3746015 by Michael.Dupuis

	#jira UE-52090: Added missing shader for forward rendering

Change 3746038 by Michael.Dupuis

	#jira UE-51494: Make sure index is valid for this instance as instances from template and instance could mismatch due to in level changes

Change 3746076 by Michael.Dupuis

	Removed unused code

Change 3746998 by Tim.Gautier

	QAGame: - Renamed UMG_RealtimePreview > UMG_InEditorPreview
	- Moved UMG_InEditorPreview into UMG > Localization, setting up for future Localization tests

Change 3747206 by Arciel.Rekman

	Linux: make UI scale more coarse to prevent unnecessary scaling (UE-52086).

	- Monitors whose physical dimensions fall in range of 80-110 DPI should still have scale=1.0.

	(Edigrating CL 3740942 from Release-4.18 to Dev-Editor)

Change 3747211 by Arciel.Rekman

	Make failure to launch SCW more apparent to the user (UE-47263).

	- Fixes frequent crash on Linux.

	(Merging 3747070 from Release-4.18 to Dev-Editor)

Change 3747726 by Tim.Gautier

	QAGame: Resubmitting updated Loc files

Change 3747872 by Tim.Gautier

	QAGame: Resubmitting compiled Loc assets

Change 3748118 by Lauren.Ridge

	Adding help text to the material layers preview tab

Change 3748398 by Lauren.Ridge

	Fixing tooltips for the material layer read-only preview

Change 3748565 by Arciel.Rekman

	Linux: fix RHIs settings being wrong when removed via project properties (UE-51203).

	- This was a deeper issue with config system than just Linux RHIs.
	- This is a better fix than one submitted to 4.18 branch in CL 3747086.

Change 3749441 by Matt.Kuhlenschmidt

	PR #4201: Fix a typo in a comment. (Contributed by dneelyep)


Change 3749442 by Matt.Kuhlenschmidt

	PR #4195: Incorrect specifier used for FText::Format (Contributed by projectgheist)


Change 3749496 by Matt.Kuhlenschmidt

	Fix static analysis

Change 3749805 by Lauren.Ridge

	Fixing reset to default on MaterialLayerParam in base material

Change 3749838 by Lauren.Ridge

	Also correctly resetting names and layer states

Change 3750442 by Mieszko.Zielinski

	Added a safety check which addresses the crash #UE4

	Note that this is a temp fix. A proper fix is making sure ConvexShapeIndices doesn't contain any duplicates and will be inplemented in Dev-Framework.

	#jira UE-52123

Change 3752266 by Arciel.Rekman

	OpenGL: remove PLATFORM_WINDOWS exceptions.

	- Discrepancy in behavior between Windows and Linux OpenGL is unhelpful for bug reproducibility.
	- VAB bug should have been fixed long ago (on both platforms).

Change 3752929 by Arciel.Rekman

	Linux: avoid crashing on unknown drivers.

	- See https://answers.unrealengine.com/questions/724100/crashes-on-startup-after-first-run.html
	- Checks in IsDeviceIntel() and such fail if the drivers were not detected.

Change 3753254 by Michael.Dupuis

	Added missing shader for shader cache

Change 3753426 by Michael.Dupuis

	#jira UE-5751: Added the possibility to change material instance exposed params at runtime using a MID for texture, vector and scalar for 1 component or all components

Change 3753440 by Alexis.Matte

	Fix fbx scene importer morph target import crash

	#jira UE-52255

Change 3753457 by Michael.Dupuis

	build fix

Change 3753700 by Chris.Bunner

	Make GetSharedInput preview fallback always available in editor as this should handle previews, thumbnails and other editor-only cases.
	Refactor to remove duplicate code in material translator.
	Material layer expressions are required for client load, fixes cooked builds using layer instances in the base material layer stack.

Change 3754760 by Chris.Bunner

	Tidying up EngineTest/ShaderModels map.

Change 3754765 by Arciel.Rekman

	Avoid placing tooltip windows under the cursor (UE-12288).

	- Fixes inability to use some corners of the screen on Linux (the tooltips there aren't click-through).

Change 3754788 by Matt.Kuhlenschmidt

	Fix details panel crash after compiling blueprints that have edit conditon properties

Change 3754933 by Christina.TempelaarL

	Fixed typo in heightLerp (transistion->transition).
	#jira UE-46736

Change 3754946 by Cody.Albert

	Update loading movie code to properly calculate delta time when throttled at 60fps

Change 3755059 by Jamie.Dale

	Fixed game preview language not updating in realtime while PIE was enabled

	#jira UE-52209

Change 3755130 by Jamie.Dale

	Fixed game preview language not updating from native when switching between preview languages

	#jira UE-52348

Change 3755664 by Michael.Dupuis

	Fixed compile warning

Change 3755714 by Yannick.Lange

	Always allow capturing thumbnails from viewport.
	This also hides the thumbnail editing UI when a thumbnail was captured from a viewport.

Change 3755944 by Alexis.Matte

	Fix crash when importing morph target with "built in" tangent option

	#jira UE-52319

Change 3756109 by Christina.TempelaarL

	fixed FBX importer Import Textures tooltip, UMaterial -> Material in tooltip text
	#jira UE-48389

Change 3756169 by Jamie.Dale

	Added plural form pattern to SContentBrowser::GetPathText

Change 3756493 by Laz.Matech

	Updating Localization content to further test InEditor Preview Language in UMG

	#jira none

Change 3758336 by Alexis.Matte

	Fix a crash when importing morph target there was a unsync between some buffer depending on the import options

	#jira UE-52319

Change 3758947 by Jamie.Dale

	Fixed cursor position regression in search boxes

	This was caused by a call to SetText that was added in CL# 3627864

	This caused the text to update as it was changed, which made the cursor jump to the end of the text and made it impossible to type in the middle of a search term.

	This was done as a bound FText value had been passed to the InitalText of SSearchBox, which made the text resolution behave strangely. InitalText should always be a value, and SSearchBox now resolves any bindings during its constructor.

	#jira UE-48874

Change 3759000 by Laz.Matech

	Submitting the .PO file for CL 3756493

	#jira none

Change 3759480 by Matt.Kuhlenschmidt

	Safe guard against brush details crash

	#jira UE-52278

Change 3759665 by Matt.Kuhlenschmidt

	PR #4214: UE-52249: Use valid PreviewShadowsIndicatorMaterialName (Contributed by projectgheist)


Change 3761211 by Matt.Kuhlenschmidt

	Remove the restriction that a level must be writable to be locked/unlocked.
	Fixed duplicate icons being used by the level browser

Change 3761304 by Chris.Bunner

	MaterialAttributeLayers graph node BaseMA input is no longer required.
	Note: Requires "Use Preview Value" set to true on inputs.

Change 3761307 by Chris.Bunner

	New material layers and blends will have "Use Preview Value" set to true by default to avoid the need for connected inputs - Missed file on previous commit.

Change 3761357 by Chris.Bunner

	Renamed material shared input enum.

Change 3761419 by Chris.Bunner

	Updating material layers automated test assets after recent changes.
	Reverted some naming changes so existing screenshot tests can be reused.

Change 3762058 by tim.gautier

	QAGame: Adding Material Layer assets for testing (Content/Materials/LayerFunction)

Change 3763328 by Matt.Kuhlenschmidt

	Fix Slate warning at editor startup

Change 3763354 by Alexis.Matte

	Fix skeletal mesh material assign when reducing a LOD that was import from a file using simplygon reduction

	#jira UE-52505

Change 3763501 by Matt.Kuhlenschmidt

	Prevent shared asset thumbnail pools from having their resources forcefully released when they are in use.  ReleaseResources is now private and only called on destruction of the pool

Change 3763574 by Matt.Kuhlenschmidt

	Fix slate material box brushes not being keyed off image size

	#jira UE-40219

Change 3763678 by Jamie.Dale

	Disable realtime level editor viewport if running under remote desktop

	This makes the main editor window much more responsive by default under remote desktop

Change 3763679 by Jamie.Dale

	Added asset caching metrics for the loc gather

Change 3763693 by Matt.Kuhlenschmidt

	Changed the code that activates the actor details tab  when selection changes to a flash. There are simply too many things that change the selection and steal focus away from a tab being used

	#jira UE-51754

Change 3763826 by Michael.Dupuis

	Fixed Fortnite cooking crash

Change 3763864 by Harrison.Moore

	Adding New Default 'LayerBlend' assets

Change 3764028 by Christina.TempelaarL

	#jira UE-47695 Auto LOD bug

	Moved the sections dropdown to the LOD Picker category and modified Custom cb behavior, based on suggestions from charlie.

Change 3764031 by Christina.TempelaarL

	#jira UE-47695 Auto LOD in StaticMeshViewer

	moved LOD combo widget to LOD picker category and hide custom checkboxes until custom checked.

Change 3764076 by tim.gautier

	QAGame: Submitting UMG_Multitouch_test for initial Multitouch testing

Change 3764263 by Matt.Kuhlenschmidt

	Fix the floor mesh thumbnail

Change 3764284 by Chris.Bunner

	Removing some asserts for cases that can validly fail and are already handled.

Change 3764372 by Matt.Kuhlenschmidt

	PR #4196: Show edit icons in editor (Contributed by projectgheist)


Change 3764388 by Chris.Bunner

	Fixing logic for material, function and instance updating active materials and instances and their editors.

Change 3764674 by Harrison.Moore

	test materials added, Blend updates

Change 3764681 by Harrison.Moore

	Adding HMtest map

Change 3766238 by Chris.Bunner

	Material layer callers need to let their internal material function calls update their inputs/outputs.

Change 3766556 by Jamie.Dale

	Fixed crashes that could happen if some of the data table panels were closed

	We now always create the underlying widgets, even if they're currently hidden from view

	#jira UE-52552

Change 3767753 by Chris.Bunner

	When rebuilding a material function instance editor we must re-create and re-apply the proxies as the expressions have likely changed. Take care to maintain local parameter changes as these have not been saved yet.

Change 3768719 by Michael.Dupuis

	#jira UE-52521: Prevent possible crash if a segment have no point or invalid point

Change 3769157 by Jamie.Dale

	Fixed incorrect text selection if selecting via double click beyond the bounds of the line

	It would previously select the second to last word, rather than the last word

	#jira UE-51609

Change 3769159 by Harrison.Moore

	Belica Test update, New layer blends updated with texture type fix.

Change 3769205 by Jamie.Dale

	Fixed Windows file save dialog not appending the correct extension when there were multiple options available

	#jira UE-52180

Change 3769220 by Harrison.Moore

	Layer blend tweaks

Change 3769292 by Jamie.Dale

	Removing redundant code

	Applying the correct package ID, only to then strip it off again is rather pointless

Change 3769479 by Arciel.Rekman

	UBT: Use response files for compiler when compiling for Linux.

	- Some command lines are too long when cross-compiling on Windows.

Change 3769920 by Arciel.Rekman

	Linux: convert yet another initialization crash to a user-friendly message (UE-52457).

	#jira UE-52457

Change 3771055 by Alexis.Matte

	Make sure we set the Used by morph target material flag to material use by the morphtarget instead of all skinned mesh component
	Use the morph vertex factory only for section that has active morph target

	#jira UE-51483

Change 3771135 by Michael.Dupuis

	Fixed fortnite cooking

Change 3773054 by Yannick.Lange

	Avoid loading viewport interaction assets when starting the editor.

Change 3774184 by Arciel.Rekman

	Linux: disabled some gdb visualizers until the issue is fixed.

	- CL 3773942 by CengizT.

Change 3774303 by Matt.Kuhlenschmidt

	Pull requests to fix various typos

Change 3774305 by Matt.Kuhlenschmidt

	PR #4237: Visual Studio Repeatedly Opens (UE-51608) (Contributed by LordNed)


	#jira UE-51608

Change 3774701 by Arciel.Rekman

	OpenGL: fix ARB callback errors when hitting Build Lighting.

	- Merged from 4.18 shelf.

Change 3775812 by Matt.Kuhlenschmidt

	Fix One-off crash undo-ing while working with Material Params / Material Functions

	#jira UE-52680

Change 3775849 by Matt.Kuhlenschmidt

	More info for UE-52610

Change 3775850 by Matt.Kuhlenschmidt

	Guard against mesh paint crashes

	#jira UE-52618

Change 3775904 by Matt.Kuhlenschmidt

	Added logging to track down window shutdown issues

Change 3775913 by Matt.Kuhlenschmidt

	Pull requests for typos

	#jira UE-52751, UE-52748

Change 3776676 by Jamie.Dale

	Fixed being able to insert tabs into spin boxes with units

	#jira UE-52776

Change 3777006 by Michael.Trepka

	Process Mac windowDidBecomeMain and windowDidResignMain notifications immediately instead of deferring them. This solves issues with Slate code that closes and immediately opens new menu windows. Previously closing a window would schedule menu parent's activation event that could be processed after another menu's creation, making it immediately disappear.

	#jira UE-52145

Change 3777170 by Arciel.Rekman

	Linux: use Xft.dpi (most desktop environments expose their scale through that) as a DPI value (UE-52086, UE-52766).

	- Change by Brandon.Schaefer.
	- Limitation: no per-monitor DPI.

	(Edigrating CL 3776509 //UE4/Release-4.18/... to //UE4/Dev-Editor/...)

Change 3777292 by Arciel.Rekman

	Linux: fix symbol collision causing problems with AMD drivers (UE-51843).

	- We cannot have elf_end() hidden, because libcuda.so calls elf_end from libnvidia-fatbinaryloader.so and this breaks linking monolithic binaries ("hidden symbol referenced in DSO").
	- We cannot have elf_end() visible, because of a name collision with a different libelf used by AMD drivers.
	- The only possible workaround is to have elf_end() renamed.

	(Edigrating CL 3777242 from //UE4/Release-4.18/... to //UE4/Dev-Editor/...)

Change 3777686 by Joe.Conley

	Blueprint editor variable type tooltips: fix case mismatch that was preventing type name to be displayed properly in soft object/class reference tooltips.

Change 3778180 by Jamie.Dale

	Avoid a crash if a regex pattern or matcher outlive ICU during shutdown

	#jira UE-52770

Change 3778182 by Jamie.Dale

	Avoid a crash if a break iterator outlives ICU during shutdown

Change 3778368 by Jamie.Dale

	Added missing pragma once

Change 3778560 by Matt.Kuhlenschmidt

	Prevent non-shared DDC notification from triggering for epic internal builds

Change 3778709 by Lauren.Ridge

	Copying 4.18.2 array reordering propagating to children fix (originally 3778547)

Change 3779205 by Lauren.Ridge

	Duplicating 3776649 to fix a crash on compile due to partial GC of a widget.

	#jira UE-52260

Change 3779217 by Brandon.Schaefer

	GitHub #3678 Fix Setup.sh handling of special characters in PNG path

	#jira UE-46103

Change 3779341 by Brandon.Schaefer

	GitHub #3012 Use elemtry OS versions to set the ubuntu versions for depencies

	#jira UE-39364

Change 3780274 by Joe.Conley

	DataTables: Add documentation tooltips showing type information to header columns

Change 3780840 by Alexis.Matte

	Do a re-import when user re-import LOD 0
	Prevent importing more then MAX_SKELETAL_MESH_LODS
	#jira UE-52373

Change 3781067 by Arciel.Rekman

	Linux: fix OSSSteam cross-compilation (and CIS).

	- Broken by previous change that put compile-time arguments into response file and bulk-replaced \ with /, which affected things like Definitions.Add("STEAM_SDK_VER_PATH=TEXT(\"Steam" + SteamVersion + "\")");

Change 3781110 by Christina.TempelaarL

	#jira UE-47695 moved Static Mesh Editor LOD menu from tools to viewport tool bar

Change 3781531 by Christina.TempelaarL

	#jira UE-47695 added LOD menu to Static Mesh Editor viewport

Change 3781663 by Alexis.Matte

	Fix for cancel export fbx when previewing animation sequence export
	#jira UE-49743

Change 3782007 by Jamie.Dale

	Improved the ability to lockdown available game languages

	In addition to the previous "DisabledCultures" array, you can now add an array of "EnabledCultures" (using the same per-build config filters) to explicitly list the cultures that are allowed to be used in your build (if the list is empty, then everything is allowed unless otherwise disabled).

	This also stops the game from attempting to fallback to the native language if the native language has been disabled (we will fallback to the first available language instead).

Change 3782122 by Jordan.Walker

	test assets for material layers

Change 3782131 by Joe.Graf

	Added support for IOS and TVOS targets when generating CMake files on Mac

Change 3782218 by Christina.TempelaarL

	fixing include paths and capitalization causing build errors.

Change 3783171 by Michael.Dupuis

	Added the possibility to override default LOD rules for visible primitive and whole scene shadow casting primitive
	Added the possibility to store Custom data per view for the frame duration

Change 3783172 by Michael.Dupuis

	#jira UE-35097	: Refactored landscape rendering logic to improve general performance while using new custom data and custom lod feature
	Exposed many new settings to control component using sub sections rendering, tessellated component, shadow should be include into tessellation, tessellation falloff based on camera location
	Changed how LOD distribution is done to be in screen size instead of distance.
	Give the possibility to have a different distribution for LOD0 vs the other one.

Change 3783174 by Michael.Dupuis

	missing file to landscape refactor

Change 3783315 by Lauren.Ridge

	Changing a parameter in the Material Parameter Panel now dirties the material correctly.
	Changing a layer parameter in the Material Instance Editor now refreshes the details panel as well.

Change 3783374 by Chris.Bunner

	Adding MaterialSharedInputCollection to hidden list when MaterialLayers disabled.

Change 3783617 by Chris.Bunner

	Added a Channel Mask material expression parameter. Wraps up a vector parameter and dot product with a single channel selection interface, internally a regular vector parameter.
	Fixed GetSharedInput failing to return the preview texture in the Function Instance editor.

Change 3783676 by Lauren.Ridge

	Adding access to a material function instance's parent in the Material Instance Editor

Change 3783751 by Michael.Trepka

	PR #4248: Fixed C++ std in generated Xcode project to match rest of engine (C++14) (Contributed by Bo98)


Change 3783971 by Brandon.Schaefer

	Rename/Move all deploy/Deploy folder/files into AlembicDeploy. Due to case sensitivity on linux, need to maintain the correct case.

	#jira UE-37631

Change 3783992 by Michael.Dupuis

	#jira UE-35097: Remove tessellation on editor tools
	Fixed a case where tessellation multiplier at 0 would cause the component to not be visible
	Fixed minor tessellation falloff calculation error

Change 3784502 by Chris.Bunner

	Restored missing bool setter.
	Coding standards fix.

Change 3784614 by Arciel.Rekman

	Linux: better error message when running out of inotify watches (UE-50459).

	#jira UE-50459

Change 3784620 by Chris.Bunner

	Updated LayerBlend_TextureMask to use new ChannelMask parameter.

Change 3784725 by Lauren.Ridge

	Fixing groups not sorting correctly

Change 3785645 by Chris.Bunner

	Non-editor compile fix.

Change 3785666 by Arciel.Rekman

	Linux: restore ability to use Wayland instead of X11.

	- SDL libs recompiled and Wayland version is set to lower one to be compatible with the compositor shipped on Ubuntu 16.04 LTS
	- Change by Anthony.Bills.

Change 3785708 by Arciel.Rekman

	Linux: implement voice capture using SDL (non-server only) (UE-51298).

	- Based on pull request #4131 by mkirzinger.

Change 3785878 by Arciel.Rekman

	UBT: add VSCode to default generators on Linux.

Change 3786058 by Arciel.Rekman

	Do not add dependencies on other RHIs for servers (UE-48894).

	- Pull request #3944 contributed by pfoote.


	#jira UE-48894

Change 3786845 by Arciel.Rekman

	Code changes to make gcc support easier (UE-51978).

	- Contributed by a licensee (pull request #4181 by tomwardio "Collection of fixes to allow UE4 to be compiled by GCC on Linux").

Change 3786871 by Matt.Kuhlenschmidt

	Fix static analysis

Change 3786883 by Matt.Kuhlenschmidt

	Fix HTML5

Change 3786923 by Matt.Kuhlenschmidt

	Fix engine layer blend asset referencing game content.  This is not allowed

	#jira UE-52888

Change 3786927 by Brandon.Schaefer

	Linux: Alembic support

	#jira: UE-37631

Change 3786994 by Arciel.Rekman

	Remove support for /-prefixed switches (UE-47429).

	- They collide with absolute file paths on Mac/Linux and a source of inconsistencies between platforms.

	#jira UE-47429

Change 3787032 by Michael.Trepka

	Initial support for building and running Niagara on Mac

Change 3787035 by Brandon.Schaefer

	GitHub #4166 Undef GL entrypoint macros after use

	#jira UE-51663

Change 3787144 by Lauren.Ridge

	Fixing material parameter group association resetting after undo

	#jira UE-52514

Change 3787839 by Jordan.Walker

	updated engine level layer blends to not include game content
	switched them to use Chris B's new mask selection node

Change 3787967 by Lauren.Ridge

	Fix for broken layer groups, related crash

Change 3787991 by Lauren.Ridge

	Fixing thumbnails for material function instances, resolving not being able to delete new function instances

	#jira UE-52967

Change 3788226 by Michael.Trepka

	Fixed a deadlock when closing Mac CrashReportClient which resulted from changes in CL 3777006

	#jira UE-53002

Change 3788557 by Brandon.Schaefer

	Fix shadow compiler warnings around our includes

Change 3789700 by Lauren.Ridge

	Experimental setting for turning on Material Layers - off by default.

Change 3789703 by Jamie.Dale

	Harden the LocMeta and LocRes loading to prevent loading files that are too new

Change 3789706 by Jamie.Dale

	Added localization ID to the package summary

	This will allow the localization gatherer to query it without having to load the entire package

Change 3789708 by Jamie.Dale

	Added a way to display and reset the package localization ID via the Content Browser

Change 3789709 by Jamie.Dale

	Added warning for duplicate package localization IDs when gathering asset localization

Change 3789713 by Jamie.Dale

	Special case zero-width space in the text shaper to avoid fonts rendering the fallback glyph

Change 3789736 by Christina.TempelaarL

	Fixed recently-introduced StaticMeshEditor bug - changing LOD dropdown menu selection was not changing LOD sections category.

Change 3789853 by Chris.Bunner

	Material instances should return overridden values when the caller is requesting the default, not leaving the request to fall through to the base material.

	#jira UE-52796

Change 3790185 by Brandon.Schaefer

	Add better error handling when using new SDK for linux on windows.

	#jira UE-50527

Change 3790195 by Jamie.Dale

	Fixed line-ending inconsistency when retrieving rich-text

	We were using LINE_TERMINATOR when getting the offsets, but \n when getting the text

Change 3790473 by Chris.Bunner

	When finding expression by GUID, only return FunctionCall result if expression was found. Fixes cases where searched-for expression is after a FunctionCall in the expressions list.

	#jira UE-52729

Change 3790650 by Arciel.Rekman

	UBT: Linux: print build details first before refusing to build.

	- Tweak to the previous feature that we forgot.

Change 3790681 by Arciel.Rekman

	UBT: Linux: tweaks to wording (SDK -> toolchain).

Change 3791459 by Brandon.Schaefer

	Linux: libcurl rebuild for version 7.57

	#jira OGS-870

Change 3791533 by Arciel.Rekman

	Better error messaging when UMB is unusable (UE-50658).

	- Should be replaced by a better fix, but merging this workaround in case the better fix will not be done in time for 4.19.

	(Edigrating CL 3789387, 3789787 from Release-4.18 to Dev-Editor)

Change 3791885 by Matt.Kuhlenschmidt

	Fix static analysis

	#jira UE-53097

Change 3791910 by Brandon.Schaefer

	Fix for not using proper libraries on x86_64 for libcurl

	#jira OGS-870

Change 3792017 by Jamie.Dale

	Fixed a conflict between the path settings and favorite settings

Change 3792022 by Jamie.Dale

	Fixed a crash that could happen when performing ARO on the property chains of a struct

	#jira UE-52999

Change 3792025 by Jamie.Dale

	Changed package summary localization ID to be versioned by the object version to avoid changing data in unversioned cooked assets

Change 3792066 by Michael.Dupuis

	#jira UE-5751: Fixed possible crash when using dynamic matrial instance
	Added missing shaders for landscape when using tessellation

Change 3792718 by Arciel.Rekman

	OpenGL: bringing back Windows workarounds to unblock copy-up (UE-52534).

	#jira UE-52534

Change 3793018 by Mike.Erwin

	new glTF importer plugin

	Import StaticMesh, Material, and Texture assets from the Khronos glTF 2.0 format. Text (.gltf) and binary (.glb) files are supported.

	Limitations:
	- There is no options UI. All assets from the file are imported.
	- The glTF format can describe animation and whole scenes, but the initial version of this plugin does not attempt either.
	- Data encoded as Base64 data URI is not supported. This is uncommon but is part of the glTF spec.

	#jira: UE-50695

Change 3793626 by Matt.Kuhlenschmidt

	Logging for HTML5 issue

Change 3794034 by Matt.Kuhlenschmidt

	Fix CIS

Change 3794271 by Michael.Dupuis

	#jira UE-53133: Fxed shadow calculation when using non whole scene shadow

Change 3794273 by Chris.Bunner

	Function call material nodes should be created with no outputs by default.

	#jira UE-53127, UE-53128, UE-52616

Change 3794334 by Lina.Halper

	- Fix animation reinitializing when just setting new animation with single node
	- Deprecated GetScalarParameterDefault and replace that with GetScalarParameterDefaultValue

	#jira: UE-52796

Change 3794338 by Michael.Dupuis

	Fixed lod visual popping when texture mip used for landscape is not ready

Change 3794350 by Mike.Erwin

	Fix glTF importer header includes.
	Build was failing on Linux.

Change 3794357 by Michael.Dupuis

	#jira UE-53166: buildfix: removed phase 2 landscape optim leftover...

Change 3794549 by Michael.Dupuis

	#jira UE-53166 : fixed compile error

Change 3794755 by Matt.Kuhlenschmidt

	Fix automation warning

Change 3794910 by Lina.Halper

	Change material default value to be red to keep the behavior same.

	- Previous code was getting default value of parent, which was wrong, but now since we're grabbing correct value, it's not doing what it meant to do. Changed content to keep same value as parent as default.

Change 3795296 by Mike.Erwin

	glTF: fix Linux build errors

	A variable was being shadowed.

	The other errors are due to an obscure corner of the C++ spec which clang enforces.
	https://stackoverflow.com/questions/21900237/do-i-really-need-to-implement-user-provided-constructor-for-const-objects

	#jira UE-53204

Change 3797313 by Chris.Bunner

	Re-built lighting in Rendering/ShaderModels automated test map and updated failing screenshots.

	#jira UE-53198

Change 3803962 by Jamie.Dale

	Fixed struct instances not comparing against the correct default values when gathering for localization

Change 3804771 by Michael.Dupuis

	Back out changelist 3783171

Change 3804772 by Michael.Dupuis

	Back out changelist 3783172

Change 3805258 by Michael.Dupuis

	Added missing shader cache for landscape

Change 3806105 by Matt.Kuhlenschmidt

	Disable harware benchmarking during automation tests, commandlets, and on the build machine

Change 3806438 by Michael.Dupuis

	#jira UE-53228: Fixed rendering path of new landscape optim when using GDoInitViewsLightingAfterPrepass(used in Orion)

Change 3806577 by Matt.Kuhlenschmidt

	Change plugin uploading to crash reporter to only do so in editor builds

Change 3806588 by Michael.Dupuis

	Remove temp test version in landscape version

Change 3806900 by Jamie.Dale

	Fixed 'inconsistent DLL linkage' error when using older versions of Python 2.7

	#jira UE-53353

Change 3807125 by Jamie.Dale

	Fixed UBT warning after Dev-Core merge

Change 3807299 by tim.gautier

	QAGame: Checking in test asset M_LandscapeMaterial_Foliage, quicker repro for UE-53442

[CL 3807911 by Matt Kuhlenschmidt in Main branch]
2017-12-14 10:07:13 -05:00

2414 lines
86 KiB
C++

// Copyright 1998-2017 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/Navigation/NavigationSystem.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 "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;
{
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( int32 LevelIndex = 0 ; LevelIndex < World->StreamingLevels.Num() ; ++LevelIndex )
{
ULevelStreaming* CurStreamingLevel = World->StreamingLevels[ LevelIndex ];
if (CurStreamingLevel && CurStreamingLevel->GetLoadedLevel() && !CurStreamingLevel->bShouldBeVisibleInEditor)
{
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;
GDebugStaticLightingInfo = FDebugLightingOutput();
}
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", "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<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