You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3175510 on 2016/10/26 by Josh.Adams - New Wolf SDK support (11). - Added new input plugin now that extra NDA is lifted Change 3176629 on 2016/10/27 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) Change 3177232 on 2016/10/27 by Josh.Adams - Minor comment change Change 3177348 on 2016/10/27 by Dmitry.Rekman Linux: default to GL4. Change 3177523 on 2016/10/27 by Dmitry.Rekman Linux: update libc++ to 3.9 and add AArch64. Change 3178208 on 2016/10/28 by Daniel.Lamb Enable multithreaded lightmap encoding. Change 3178273 on 2016/10/28 by Luke.Thatcher [PLATFORM] [PS4] [!] Fix crash in PS4 packaging step. - Parallel-for accessing the same log files, causing IOException. Change 3178573 on 2016/10/28 by Dmitry.Rekman Linux: fix for projects not having proper version associations (UE-5954). - Fixed by CengizT. Change3180487on 2016/10/31 by Josh.Adams Moved new file to peoper spot Change 3180508 on 2016/10/31 by Josh.Adams - Fixed crash on audio free for Wolf Change 3181821 on 2016/11/01 by Josh.Adams - Fixed ShooterGame cooking after sync from main Change 3182469 on 2016/11/01 by Josh.Adams - test/shipping build wolf fixes Change 3183078 on 2016/11/02 by Josh.Adams - Added AllDesktop back in for Windows (File | Package) Change 3183229 on 2016/11/02 by Josh.Adams - Fixed wrong path in JunkManifest.txt Change 3184245 on 2016/11/02 by Dmitry.Rekman Linux: add AArch64 (ARM64) libs. Change 3184326 on 2016/11/02 by Dmitry.Rekman Linux: add more files for AArch64. Change 3184353 on 2016/11/02 by Dmitry.Rekman Linux: Add missed AArch64 libpng. Change 3184871 on 2016/11/03 by Luke.Thatcher [PLATFORM] [PS4] [!] Fix broken DownloadImage blueprint node on PS4. - Node should return a UTexture2DDynamic, otherwise the RHI assumes the data has been pre-formatted for the GPU, and we get pitch/layout issues. #jira UE-36365 Change 3185407 on 2016/11/03 by Dmitry.Rekman Linux: fix PhysX on AArch64. (Edigrating 3184484 from Wombat to Dev-Platform). Change 3187488 on 2016/11/04 by Josh.Adams Copying //Tasks/UE4/Private-Platform-Switch to Dev-Platform-Minimal (//UE4/Dev-Platform-Minimal) Change 3187740 on 2016/11/04 by Josh.Adams - Re-copying the Switch files, now with proper case in the directory names Change 3188304 on 2016/11/07 by Dan.Mahashin - Removed deprecated functions in NVN window creation Change3188865on 2016/11/07 by Luke.Thatcher [PLATFORM] [PS4] [~] Move PS4 console input handler into engine classes from OrionGame. - Enables console input from Sony's "Console Output" tool for all games, in debug/development builds. #jira UE-37672 Change 3189517 on 2016/11/07 by Jeff.Campeau Fix incorrect local platform identification in device manager. #jira UE-38312 Change 3189897 on 2016/11/08 by Luke.Thatcher [PLATFORM] [!] Fix width/height mismatch in DownloadImage blueprint node. Change 3190042 on 2016/11/08 by Josh.Adams - Fixed default and Shooter App Ids for Switch Change 3190181 on 2016/11/08 by Joe.Barnes [UE-37275] Split reflection capture error message into two UE_LOG()s. Line length causes truncation and line wrap on some platforms. Change 3190185 on 2016/11/08 by Joe.Barnes Fix another instance of UE_LOG() where the string was being truncated on Switch platform. Change 3190272 on 2016/11/08 by Daniel.Lamb Add file hashes to depependency tracking info. Moved partial gc controlling code outside of the cooker. Store cooked file hashes in cooked asset registry. Cooked asset registry is now part of the cooker instead of chunking manifest. #test cook paragon Change 3190332 on 2016/11/08 by Omar.Rodriguez Fixing issues with iOS remote notifications * Update UPlatformGameInstance::FPlatformRegisteredForRemoteNotificationsDelegate signature so the parameter is const& which will work with BlueprintAssignable * Fix misspelling when doing respondsToSelector check * Update generated Xcode project to use the generated entitlements file * Add remote-notification as a background mode * Update the generated entitlements file contents to include APS environment for push notifications * Added bEnableRemoteNotificationsSupport ini parameter to control whether iOS push notifications code is compiled Change 3190391 on 2016/11/08 by Brent.Pease UE-31739 - Crash when Deploying to iPad Air with BC4 Texture Compression Setting (Josh's suggestion worked out of the box) Change 3190786 on 2016/11/08 by Bart.Hawthorne Fix some missing PLATFORM_WOLF changes to PLATFORM_SWITCH in ShooterGame Change 3190902 on 2016/11/08 by Alicia.Cano Allow RTTI and exceptions to be enabled for Android #jira UE-37845 #android Change 3190973 on 2016/11/08 by Chris.Babcock Add ability to set element value field with new text parameter for UPL #jira UE-37390 #PR #2869 #ue4 #upl Change 3191411 on 2016/11/09 by Josh.Stoddard Warn when user tries to use a shared pak reader on the wrong thread #jira UE-38049 Change 3191635 on 2016/11/09 by Josh.Stoddard More useful message during cook when AT9 assets fail to encode using SCE's tool #jira UE-38053 Change 3191663 on 2016/11/09 by Peter.Sauerbrei fix for ios build from PC Change 3191701 on 2016/11/09 by Brent.Pease implement iOS device logs on windows Change 3191794 on 2016/11/09 by Daniel.Lamb Fixed up compile error missing header file. #test Compile editor #jira UE-38414 Change 3191807 on 2016/11/09 by Josh.Adams - Fixed one chage that was missed in the WolfPlat->Switch rename Change 3191867 on 2016/11/09 by Josh.Adams - Enabled Switch for ShooterGame project Change 3191958 on 2016/11/09 by Jeff.Campeau Add warning for anyone still using XP Change 3192185 on 2016/11/09 by Josh.Adams - Updated to SDK 0.11.12 - Added TrackLotCheckItem API to track guidelines with limits (nothing using it yet) Change 3192241 on 2016/11/09 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) Change 3192324 on 2016/11/09 by Josh.Adams - Worked around an issue with RunOnTarget stripping quotes around paths with spaces #jira UE-38388 Change 3192387 on 2016/11/09 by Josh.Adams - Updating editor icon for Switch #jira UE-38295 Change 3192476 on 2016/11/09 by Dmitry.Rekman Linux: put correct OpenAL lib per architecture. (Edigrating CL 3185947 from Wombat to Dev-Platform) Change 3192527 on 2016/11/09 by Josh.Adams - Fixed a shadow variable warning #jira UE-38408 Change 3192606 on 2016/11/09 by Jeff.Campeau XP option removed #jira UEPLAT-1542 Change 3192644 on 2016/11/09 by Josh.Adams - Fixed a CIS error (not sure why I don't get it, but hey) Change 3192659 on 2016/11/09 by Josh.Adams - Fixed a crash in DeploymentServer Change 3192672 on 2016/11/09 by Jeff.Campeau Fix WinXP message spamming Change 3193252 on 2016/11/10 by Josh.Adams - Remove assertion in SwitchTextureFormat when the SDK can't be found (if you are sharing DLLs) Change 3193756 on 2016/11/10 by Dmitry.Rekman Linux: add support for touch events. (Edigrating CL 3188159 from Wombat to Dev-Platform). Change 3194297 on 2016/11/10 by Jeff.Campeau HarfBuzz implementation for Xbox One #jira UE-28590 Change 3194299 on 2016/11/10 by Jeff.Campeau Pump Xbox One messaging during slow startup tasks #jira UEPLAT-1276 Change 3194300 on 2016/11/10 by Jeff.Campeau Use response files when building for Xbox One #jira UEPLAT-1296 Change 3194313 on 2016/11/11 by Jeff.Campeau Stop uploading symbols on the first error Show a more detailed error message if symbol uploading fails Add a command line option to disable upload of symbols #1852 #jira UE-24425 Change 3194327 on 2016/11/11 by Jeff.Campeau Deduplicate Xbox One build.cs setup for several modules #jira UE-37540 Change 3194402 on 2016/11/11 by Dmitry.Rekman Linux: do not apply mouse workaround unnecessarily. - Only matters when there is more than one window. (Edigrating 3194399 from Wombat to Dev-Platform). Change 3194434 on 2016/11/11 by Dan.Mahashin - Ported fix CL 3193690: Add workaround to file I/O error about ResultInvalidCurrentMemory when reloading levels - remove uncached attribute during memory pool finalization Change 3194569 on 2016/11/11 by Daniel.Lamb Fixed issue with CreateLinker failing to return LinkerLoad but creating a UPackage which can't be cleaned up. Change 3194570 on 2016/11/11 by Daniel.Lamb Fix for DiffSerializeArchive not using the correct archive when saving packages. #test Cook paragon Change 3194571 on 2016/11/11 by Daniel.Lamb Make sure dependent packages are valid before using them. Added FastBuildCookRun bat file for paragon testing. #test Cook Paragon Change 3194575 on 2016/11/11 by Daniel.Lamb Reworked a warning for the cooker. Change 3194698 on 2016/11/11 by Daniel.Lamb Skip skin verify only runs on build machines now. Saves paragon cook time. Change 3194699 on 2016/11/11 by Daniel.Lamb Changed the wording of skip editor content setting so it's more clear. #test none Change 3194702 on 2016/11/11 by Daniel.Lamb Potential fix for default materials not being in chunk zero. #test run ps4 cooked build paragon Change3194711on 2016/11/11 by Alicia.Cano Allow RTTI and exceptions to be enabled for Android Allow RTTI to be enabled for IOS, Mac #jira UE-37845, UE-20314 #android #ios #mac Change 3194956 on 2016/11/11 by Josh.Adams - Removed the crash with unknown socket error code, left in the warning Change 3195028 on 2016/11/11 by Dmitry.Rekman Linux: repair launch on. (Edigrating 3194384 from //UE4/Private-Wombat/... to //UE4/Dev-Platform/...) Change 3195041 on 2016/11/11 by Dmitry.Rekman Linux: support selecting architectures per project. (Edigrating 3192783 from Wombat to Dev-Platform). Change 3195058 on 2016/11/11 by Dmitry.Rekman Linux: fix code to determine number of cores. - ARM topology seems not to be in line with the assumptions made by x86-centric code. (Merging 3184632 from Wombat to Dev-Platform). Change 3195082 on 2016/11/11 by Josh.Adams - Fixed name of packaged Switch builds to have the config in it (Shipping, etc) #jira UE-38394 Change 3195151 on 2016/11/11 by Bart.Hawthorne - Add game server settings to project settings to connect to the actual game server, instead of the debug login - Use the system software dialog box to show error codes for login failures Change 3195153 on 2016/11/11 by Josh.Adams - Fixed copy and paste logs errors Change 3195156 on 2016/11/11 by Josh.Adams - Fixed some default values, especially for save games (uses their default of 4MB size) - Added some LotCheck write tracking Change 3195285 on 2016/11/11 by Jeff.Campeau Fix HarfBuzz warning on Xbox One Change 3195477 on 2016/11/11 by Josh.Adams - Fixed up some IsGameOnly calls #jira UE-37575 Change 3195490 on 2016/11/11 by Dmitry.Rekman UAT: fix CIS (removed old variables). Change 3195724 on 2016/11/11 by Josh.Adams - Final fix for name of .nsp (content only projects in Shipping config, etc) #jira UE-38394 Change 3195755 on 2016/11/11 by Josh.Adams - Made translucent Switch icons Change 3195771 on 2016/11/11 by Josh.Adams - Fixed some Switch "space in path" issues #jira UE-38393 Change 3195801 on 2016/11/11 by Josh.Adams - Handle making sure the save is completed before we shutdown #jira UE-38215 Change 3196593 on 2016/11/14 by Michael.Trepka Implemented Info string in AvfMedia for display in Media Player Editor #jira UE-35386 Change 3196782 on 2016/11/14 by Josh.Adams - Added a comment for a workaround Change 3196784 on 2016/11/14 by Michael.Trepka Alembic importer for Mac #jira UE-37708 Change 3196901 on 2016/11/14 by Alicia.Cano ADB over wifi fails to deploy on Launch on. #jira UE-37957 #android Change 3197055 on 2016/11/14 by Josh.Adams - Fixed BinnedAllocator crash that happened with PoisonProxy and large allocations with large alignment Change 3197059 on 2016/11/14 by Josh.Adams - Removed some stat code with no STATS Change 3197066 on 2016/11/14 by Josh.Adams - Fixed the generic growableallocator to not free metadata before it's used for stats, and cleaned up a couple minor things Change 3197176 on 2016/11/14 by Josh.Adams - Added some helper scripts to switch in and out of debug mode on Switch Change 3197183 on 2016/11/14 by Bart.Hawthorne Error dialog fixes based on peer review feedback from JoshA Change 3197339 on 2016/11/14 by Josh.Adams Allow -htcs on the commandline now to override disabling Htcs in packaged builds Change 3197401 on 2016/11/14 by Josh.Adams - Fixed the Switch package installation script to remove the path of the package, since it causes problems with spaces, and also it makes the script less portable! #jira UE-38556 Change 3197691 on 2016/11/14 by Dmitry.Rekman Linux: save added devices. (Edigrating 3196529 from Wombat to Dev-Platform). Change 3197854 on 2016/11/15 by Dan.Mahashin - MemoryProfiler2: fixed Switch parser file path in the csproj Change 3197960 on 2016/11/15 by Dan.Mahashin - NVN RHITransitionResources() directly uses a barrier instead of relying on CopyToResolveTarget() side effect (UE-33834) Change 3198488 on 2016/11/15 by Bart.Hawthorne Submit missing NoRedist/DefaultEngine.ini file Change 3198970 on 2016/11/15 by Michael.Trepka Don't try to use installed Mono 4.6 on Mac as it's known to have issues on macOS 10.12 (for example building the editor with UBT often fails with Mono running out of file desriptors) Change 3199050 on 2016/11/15 by Daniel.Lamb Some more output to help track down iterative cooking scenarios #test Cook paragon Change 3199097 on 2016/11/15 by Josh.Adams - Fixed up Switch packaging to re-generate the meta data in case it changed since compile time (esp with content only projects - Fixed default Program Id in code - Fixed a problem with Run with a space in the path #jira UE-38608 Change 3199181 on 2016/11/15 by Dmitry.Rekman Fix CIS (compiling LinuxTargetDevice without engine). Change 3199253 on 2016/11/15 by Dmitry.Rekman Hopeful fix for a static analysis warning. Change 3199325 on 2016/11/15 by Joe.Barnes Start a new CommandBuffer immediately upon ending one. Prevents fetching when there's no CommandBuffer. Needed for Loading Screen movie playback. Change 3199814 on 2016/11/15 by Dmitry.Rekman Linux: remove forced -windowed when launching. (Merging CL 3199789 from Wombat to Dev-Platform) Change 3200580 on 2016/11/16 by Josh.Adams Updasted DeploymentServer Change 3200595 on 2016/11/16 by Joe.Barnes Removed inadvertent SleepThread() when starting movie playback. Change 3200604 on 2016/11/16 by Josh.Adams - Added NN_MIDDLEWARE macros to tag ths apps as using UE4 middleware Change 3200632 on 2016/11/16 by Brent.Pease Update PlatformShowcase with latest tests Change 3200704 on 2016/11/16 by Dmitry.Rekman Linux: fix native compilation. Change 3200711 on 2016/11/16 by Brent.Pease - Support ios audio streaming from disk - Flushed out ADPCMAudioInfo to be more flexible with buffer management in addition to support streaming from disk. This should make more code platform independent. + Other platforms should work fine but will need to be updated to use the new buffer flexability (and hence simplify their own code and buffer management) - IOS audio implementation simplified to use new ADPCMAudioInfo functionality - Fixed adpcm seamless looping NOTE: While everything works with my testing (admittedly simple tests) a little more code cleanup needs to happen... Change 3201015 on 2016/11/16 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) Change 3201023 on 2016/11/16 by Josh.Stoddard Fix splash screen assignment for iPad #jira UE-38623 Change 3201215 on 2016/11/16 by Brent.Pease Hopefully final fix for build breakage Change 3201259 on 2016/11/16 by Josh.Adams - Removed the clock rate settings from the Project Settings, and the cvars - it was just confusing - Further improved the metadata recreation during packaging (can get rid of the temp meta/desc files now I believe) - Reduced audio pool alignment to not waste massive memory from it Change3202332on 2016/11/17 by Daniel.Lamb Changed build scripts to support iterative cooking #test Ran new build scripts Change 3202371 on 2016/11/17 by Michael.Trepka Changed FAppleHttpResponse::GetContentLength to return expected content size instead of payload size so it's consistent with other implementations #jira UE-38392 Change 3202421 on 2016/11/17 by Michael.Trepka Decrease the number of max open files for a thread on Apple platforms from 256 to 192 to leave more file descriptors to Cocoa #jira UE-18343 Change 3202462 on 2016/11/17 by Michael.Trepka Fixed HTTP If-None-Match response code on Mac and iOS Fixed by iktomi, https://answers.unrealengine.com/questions/492514/http-if-none-match-logic-isnt-working-on-mac-due-t.html #jira UE-36317 Change 3202620 on 2016/11/17 by Daniel.Lamb Fixed issue with some objects being garbage collected which shouldn't be because the collection handler didn't get registered. Commandlets now do not always have GIsRequestingExit true. Made crash handler check for commandlets running and exit appropriately. #test Rebuild lighting QAGame Change 3202955 on 2016/11/17 by Daniel.Lamb Add support for clearing all the cached cooked platform data for a platform when requested. #test cook QA game #jira UE-38361 Change 3202983 on 2016/11/17 by Daniel.Lamb Added support to rebuild lightmaps commandlet for building lightmaps in seperate files. #test rebuild lighting Custom QAGame maps. #jira OR-31907 Change 3203128 on 2016/11/17 by Josh.Adams - Fixed split screen user selection in ShooterGame (brought over some changes from NickD for it as well) Change 3203537 on 2016/11/18 by Dmitry.Rekman Fix ProjectWorldToScreen node for letterboxed viewports. (Merging CL 3201546 from Wombat to Dev-Platform). Change 3203540 on 2016/11/18 by Dmitry.Rekman Linux: be more verbose when setting vblank sync. (Merging CL 3199633 from Private-Wombat to Dev-Platform). Change 3203599 on 2016/11/18 by Dmitry.Rekman Speedup bForceCompilationAtStartup=True when nothing changed (UE-37067). - PR #2849: Contributed by slonopotamus. Change 3203610 on 2016/11/18 by Dmitry.Rekman Add CEF support for Linux (UE-6743). Change 3203615 on 2016/11/18 by Dmitry.Rekman Linux: fix bootstrap script so it is independent of working dir (UE-37016). - PR #2842 contributed by slonopotamus Change 3203645 on 2016/11/18 by Dmitry.Rekman Linux: fix UnrealCEFSubProcess. Change 3203658 on 2016/11/18 by Dmitry.Rekman Remove hard-coded paths to mono binary (UE-35228). - Another way to implement pull request #2741. Change 3203770 on 2016/11/18 by Josh.Adams - Brought over some changes from Dev-Core to not crash in AsyncLoading with debug code Change 3204244 on 2016/11/18 by Dmitry.Rekman Unsuppress mistakenly suppressed warnings and fix one more (UE-38788). Change 3204277 on 2016/11/18 by Brent.Pease + Fix seamless looping bug found on Dan's QAGame test + Fix static analyzer warning (which was a real bug with uncompressed streaming) + Code review feedback from Aaron + Small addition from channel sync ios bug fix Change 3204576 on 2016/11/18 by Omar.Rodriguez Expose the bEnableRemoteNotificationsSupport ini setting in the iOS project settings. Change 3204629 on 2016/11/18 by Chris.Babcock Fix case of VulkanSwapChain.h #include #jira UE-38843 #ue4 #vulkan Change 3204708 on 2016/11/18 by Josh.Adams - Set SwitchMoviePlayer to include the libs from the proper directory Change 3204730 on 2016/11/18 by Josh.Adams - Changed a check to a checkf to narrow down why FMaterialUniformExpressionType::GetTypeMap().FindRef(TypeName) is returning nullptr on tvOS Change 3204865 on 2016/11/18 by Brent.Pease + Turn off ios console logs on Windows to help sort through ios packaging and launch-on issues - This is NOT a fix but it should make it easier to track down the problem with it off. Change 3204883 on 2016/11/18 by Dmitry.Rekman Linux: fix native LaunchOn (UE-38616). Change 3204914 on 2016/11/18 by Brent.Pease + Turn off the device check to prevent it from conflicting with remote packaging/launch-on Change 3204940 on 2016/11/18 by Josh.Adams Backing out changes to the profiler for Switch. Shouldn't have checked it in today during smoke Change 3204952 on 2016/11/18 by Dmitry.Rekman Linux: fix bootstrap script (UE-38851). - Caused by UE-37016. Change 3205630 on 2016/11/21 by Brent.Pease + Fix audio sound queuing bug by ensuring audio buffers are not reused by different sound source objects. + Cleaned up the locking mechanism around stopping sound sources to make its intent and function are clear + Cleaned up memory tracking and freeing. #jira ue-38846 Change 3205787 on 2016/11/21 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) [CL 3206922 by Josh Adams in Main branch]
2358 lines
85 KiB
C++
2358 lines
85 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
StaticLightingSystem.cpp: Bsp light mesh illumination builder code
|
|
=============================================================================*/
|
|
|
|
#include "UnrealEd.h"
|
|
|
|
FSwarmDebugOptions GSwarmDebugOptions;
|
|
|
|
#include "Lightmass/LightmassCharacterIndirectDetailVolume.h"
|
|
#include "LightingBuildOptions.h"
|
|
#include "StaticLightingPrivate.h"
|
|
#include "Database.h"
|
|
#include "Sorting.h"
|
|
#include "ModelLight.h"
|
|
#include "PrecomputedLightVolume.h"
|
|
#include "LevelUtils.h"
|
|
#include "CrashTracker.h"
|
|
#include "EngineModule.h"
|
|
#include "LightMap.h"
|
|
#include "ShadowMap.h"
|
|
#include "RendererInterface.h"
|
|
#include "EditorBuildUtils.h"
|
|
#include "ComponentRecreateRenderStateContext.h"
|
|
#include "Engine/LODActor.h"
|
|
#include "EditorLevelUtils.h"
|
|
|
|
DEFINE_LOG_CATEGORY(LogStaticLightingSystem);
|
|
|
|
#include "Toolkits/AssetEditorManager.h"
|
|
|
|
#include "../Lightmass/Lightmass.h"
|
|
#include "Editor/StatsViewer/Public/StatsViewerModule.h"
|
|
#include "MessageLog.h"
|
|
#include "SNotificationList.h"
|
|
#include "NotificationManager.h"
|
|
#include "Engine/GeneratedMeshAreaLight.h"
|
|
#include "Engine/LevelStreaming.h"
|
|
#include "Engine/Selection.h"
|
|
#include "Components/SkyLightComponent.h"
|
|
#include "Components/LightmassPortalComponent.h"
|
|
#include "Runtime/CoreUObject/Public/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().GetOwnedPointer() && 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.AddZeroed();
|
|
StaticLightingSystems.Last() = new FStaticLightingSystem(Options, GWorld, Level);
|
|
}
|
|
}
|
|
|
|
if (StaticLightingSystems.Num() == 0)
|
|
{
|
|
StaticLightingSystems.AddZeroed();
|
|
StaticLightingSystems.Last() = new FStaticLightingSystem(Options, GWorld, NULL);
|
|
}
|
|
|
|
ActiveStaticLightingSystem = StaticLightingSystems[0];
|
|
|
|
bool bSuccess = ActiveStaticLightingSystem->BeginLightmassProcess();
|
|
|
|
if (bSuccess)
|
|
{
|
|
SendProgressNotification();
|
|
}
|
|
else
|
|
{
|
|
DestroyStaticLightingSystems();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Tell the user that they must close their current build first.
|
|
FNotificationInfo Info( LOCTEXT("LightBuildInProgressWarning", "A lighting build is already in progress! Please cancel it before triggering a new build.") );
|
|
Info.ExpireDuration = 5.0f;
|
|
TSharedPtr<SNotificationItem> Notification = FSlateNotificationManager::Get().AddNotification(Info);
|
|
if (Notification.IsValid())
|
|
{
|
|
Notification->SetCompletionState(SNotificationItem::CS_Fail);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FStaticLightingManager::UpdateBuildLighting()
|
|
{
|
|
if (ActiveStaticLightingSystem != NULL)
|
|
{
|
|
// Note: UpdateLightingBuild can change ActiveStaticLightingSystem
|
|
ActiveStaticLightingSystem->UpdateLightingBuild();
|
|
|
|
if (ActiveStaticLightingSystem && ActiveStaticLightingSystem->CurrentBuildStage == FStaticLightingSystem::Finished)
|
|
{
|
|
ActiveStaticLightingSystem = NULL;
|
|
StaticLightingSystems.RemoveAt(0);
|
|
|
|
if (StaticLightingSystems.Num() > 0)
|
|
{
|
|
ActiveStaticLightingSystem = StaticLightingSystems[0];
|
|
|
|
bool bSuccess = ActiveStaticLightingSystem->BeginLightmassProcess();
|
|
|
|
if (bSuccess)
|
|
{
|
|
SendProgressNotification();
|
|
}
|
|
else
|
|
{
|
|
DestroyStaticLightingSystems();
|
|
}
|
|
}
|
|
}
|
|
|
|
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->DumpUnbuiltLightIteractions(*GLog);
|
|
|
|
// Update reflection captures now that static lighting has changed
|
|
// Update sky light first because it's considered direct lighting, sky diffuse will be visible in reflection capture indirect specular
|
|
World->UpdateAllSkyCaptures();
|
|
World->UpdateAllReflectionCaptures();
|
|
}
|
|
}
|
|
|
|
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)
|
|
, bCrashTrackerOriginallyEnabled(false)
|
|
, World(InWorld)
|
|
, LightingScenario(InLightingScenario)
|
|
, LightmassProcessor(NULL)
|
|
{
|
|
}
|
|
|
|
FStaticLightingSystem::~FStaticLightingSystem()
|
|
{
|
|
if (bCrashTrackerOriginallyEnabled)
|
|
{
|
|
// Re-enable the crash tracker if we ever disabled it
|
|
ICrashTrackerModule* CrashTracker = FModuleManager::LoadModulePtr<ICrashTrackerModule>("CrashTracker");
|
|
if (CrashTracker)
|
|
{
|
|
CrashTracker->SetCrashTrackingEnabled(true);
|
|
bCrashTrackerOriginallyEnabled = false;
|
|
}
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
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->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);
|
|
}
|
|
}
|
|
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\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.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, LOCTEXT("FailedToConnectToSwarmDialogMessage", "Failed to connect to Swarm."));
|
|
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;
|
|
|
|
if (!IsRunningCommandlet())
|
|
{
|
|
// Crash tracker interferes with performance during export only.
|
|
// Disable it only for export, for everything else it shouldn't matter.
|
|
// This is a very special case, and doing this sort of thing
|
|
// is almost never recommended, especially without profiling heavily.
|
|
// The reason it works here is because amortized export flushes the render
|
|
// commands every tick, which is highly detrimental to the crash tracker's operation.
|
|
// ALSO NOTE: The reason this is set here rather than be a common API in the crashtracker
|
|
// module is to discourage people from doing this sort of thing all over the place.
|
|
ICrashTrackerModule* CrashTracker = FModuleManager::LoadModulePtr<ICrashTrackerModule>("CrashTracker");
|
|
if (CrashTracker)
|
|
{
|
|
bCrashTrackerOriginallyEnabled = CrashTracker->IsCurrentlyCapturing();
|
|
CrashTracker->SetCrashTrackingEnabled(false);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
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."));
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (bCrashTrackerOriginallyEnabled)
|
|
{
|
|
// Re-enable the crash tracker if we disabled it
|
|
ICrashTrackerModule* CrashTracker = FModuleManager::LoadModulePtr<ICrashTrackerModule>("CrashTracker");
|
|
if (CrashTracker)
|
|
{
|
|
CrashTracker->SetCrashTrackingEnabled(true);
|
|
bCrashTrackerOriginallyEnabled = false;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
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::HasGameName();
|
|
|
|
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::GameDir(), 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
|