From eb84bb298d2b95aec7b2ae12cbf25ac64f25379a Mon Sep 17 00:00:00 2001 From: tylermurphy534 Date: Sun, 6 Nov 2022 15:12:42 -0500 Subject: move to self host --- .../x64/Bakery/scripts/BakeryEditorAssembly.asmdef | 16 + .../scripts/BakeryEditorAssembly.asmdef.meta | 8 + .../x64/Bakery/scripts/ftAdditionalConfig.cs | 68 + .../x64/Bakery/scripts/ftAdditionalConfig.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftBuildGraphics.cs | 7944 ++++++++++++++ .../x64/Bakery/scripts/ftBuildGraphics.cs.meta | 8 + .../Editor/x64/Bakery/scripts/ftBuildLights.cs | 1118 ++ .../x64/Bakery/scripts/ftBuildLights.cs.meta | 8 + .../Editor/x64/Bakery/scripts/ftClearMenu.cs | 153 + .../Editor/x64/Bakery/scripts/ftClearMenu.cs.meta | 12 + .../Assets/Editor/x64/Bakery/scripts/ftClient.cs | 505 + .../Editor/x64/Bakery/scripts/ftClient.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftCreateMenu.cs | 96 + .../Editor/x64/Bakery/scripts/ftCreateMenu.cs.meta | 12 + .../Assets/Editor/x64/Bakery/scripts/ftDDS.cs | 56 + .../Assets/Editor/x64/Bakery/scripts/ftDDS.cs.meta | 12 + .../Assets/Editor/x64/Bakery/scripts/ftDefine.cs | 41 + .../Editor/x64/Bakery/scripts/ftDefine.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftDetectSettings.cs | 261 + .../x64/Bakery/scripts/ftDetectSettings.cs.meta | 12 + .../x64/Bakery/scripts/ftDirectLightInspector.cs | 418 + .../Bakery/scripts/ftDirectLightInspector.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftErrorCodes.cs | 266 + .../Editor/x64/Bakery/scripts/ftErrorCodes.cs.meta | 12 + .../Bakery/scripts/ftFixResettingGlobalsOnSave.cs | 32 + .../scripts/ftFixResettingGlobalsOnSave.cs.meta | 12 + .../x64/Bakery/scripts/ftLMGroupInspector.cs | 94 + .../x64/Bakery/scripts/ftLMGroupInspector.cs.meta | 12 + .../Bakery/scripts/ftLMGroupSelectorInspector.cs | 166 + .../scripts/ftLMGroupSelectorInspector.cs.meta | 12 + .../x64/Bakery/scripts/ftLightMeshInspector.cs | 671 ++ .../Bakery/scripts/ftLightMeshInspector.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftLightingDataGen.cs | 202 + .../x64/Bakery/scripts/ftLightingDataGen.cs.meta | 12 + .../Bakery/scripts/ftLightmappedPrefabInspector.cs | 117 + .../scripts/ftLightmappedPrefabInspector.cs.meta | 12 + .../Bakery/scripts/ftLightmapsStorageInspector.cs | 29 + .../scripts/ftLightmapsStorageInspector.cs.meta | 12 + .../x64/Bakery/scripts/ftModelPostProcessor.cs | 418 + .../Bakery/scripts/ftModelPostProcessor.cs.meta | 12 + .../x64/Bakery/scripts/ftPointLightInspector.cs | 835 ++ .../Bakery/scripts/ftPointLightInspector.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftRenderLightmap.cs | 10681 +++++++++++++++++++ .../x64/Bakery/scripts/ftRenderLightmap.cs.meta | 8 + .../x64/Bakery/scripts/ftRestorePaddingMenu.cs | 88 + .../Bakery/scripts/ftRestorePaddingMenu.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftSavePadding.cs | 55 + .../x64/Bakery/scripts/ftSavePadding.cs.meta | 12 + .../x64/Bakery/scripts/ftSaveSettingsMenu.cs | 53 + .../x64/Bakery/scripts/ftSaveSettingsMenu.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftSavedPadding2.cs | 8 + .../x64/Bakery/scripts/ftSavedPadding2.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftSceneView.cs | 171 + .../Editor/x64/Bakery/scripts/ftSceneView.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftSectorInspector.cs | 474 + .../x64/Bakery/scripts/ftSectorInspector.cs.meta | 12 + .../x64/Bakery/scripts/ftSettingsProvider.cs | 69 + .../x64/Bakery/scripts/ftSettingsProvider.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftShaderTweaks.cs | 777 ++ .../x64/Bakery/scripts/ftShaderTweaks.cs.meta | 12 + .../x64/Bakery/scripts/ftSkyLightInspector.cs | 360 + .../x64/Bakery/scripts/ftSkyLightInspector.cs.meta | 12 + .../x64/Bakery/scripts/ftTextureProcessor.cs | 57 + .../x64/Bakery/scripts/ftTextureProcessor.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftUVGBufferGen.cs | 595 ++ .../x64/Bakery/scripts/ftUVGBufferGen.cs.meta | 12 + .../Assets/Editor/x64/Bakery/scripts/ftUpdater.cs | 378 + .../Editor/x64/Bakery/scripts/ftUpdater.cs.meta | 12 + .../Editor/x64/Bakery/scripts/ftVolumeInspector.cs | 226 + .../x64/Bakery/scripts/ftVolumeInspector.cs.meta | 12 + .../Assets/Editor/x64/Bakery/scripts/xatlas.meta | 9 + .../x64/Bakery/scripts/xatlas/xatlas-license.txt | 23 + .../Bakery/scripts/xatlas/xatlas-license.txt.meta | 8 + .../Editor/x64/Bakery/scripts/xatlas/xatlas.cs | 289 + .../x64/Bakery/scripts/xatlas/xatlas.cs.meta | 12 + .../x64/Bakery/scripts/xatlas/xatlasEnable.cs | 13 + .../x64/Bakery/scripts/xatlas/xatlasEnable.cs.meta | 12 + .../Editor/x64/Bakery/scripts/xatlas/xatlasLib.dll | Bin 0 -> 184832 bytes .../x64/Bakery/scripts/xatlas/xatlasLib.dll.meta | 28 + 79 files changed, 28296 insertions(+) create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/BakeryEditorAssembly.asmdef create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/BakeryEditorAssembly.asmdef.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftAdditionalConfig.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftAdditionalConfig.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildGraphics.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildGraphics.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildLights.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildLights.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClearMenu.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClearMenu.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClient.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClient.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftCreateMenu.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftCreateMenu.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDDS.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDDS.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDefine.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDefine.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDetectSettings.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDetectSettings.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDirectLightInspector.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDirectLightInspector.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftErrorCodes.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftErrorCodes.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftFixResettingGlobalsOnSave.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftFixResettingGlobalsOnSave.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupInspector.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupInspector.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupSelectorInspector.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupSelectorInspector.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightMeshInspector.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightMeshInspector.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightingDataGen.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightingDataGen.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmappedPrefabInspector.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmappedPrefabInspector.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmapsStorageInspector.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmapsStorageInspector.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftModelPostProcessor.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftModelPostProcessor.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftPointLightInspector.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftPointLightInspector.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRenderLightmap.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRenderLightmap.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRestorePaddingMenu.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRestorePaddingMenu.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavePadding.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavePadding.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSaveSettingsMenu.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSaveSettingsMenu.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavedPadding2.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavedPadding2.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSceneView.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSceneView.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSectorInspector.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSectorInspector.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSettingsProvider.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSettingsProvider.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftShaderTweaks.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftShaderTweaks.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSkyLightInspector.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSkyLightInspector.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftTextureProcessor.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftTextureProcessor.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUVGBufferGen.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUVGBufferGen.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUpdater.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUpdater.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftVolumeInspector.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftVolumeInspector.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas-license.txt create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas-license.txt.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasEnable.cs create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasEnable.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasLib.dll create mode 100644 VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasLib.dll.meta (limited to 'VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts') diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/BakeryEditorAssembly.asmdef b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/BakeryEditorAssembly.asmdef new file mode 100644 index 00000000..0aa416f0 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/BakeryEditorAssembly.asmdef @@ -0,0 +1,16 @@ +{ + "name": "BakeryEditorAssembly", + "references": [ + "BakeryRuntimeAssembly" + ], + "optionalUnityReferences": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +} \ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/BakeryEditorAssembly.asmdef.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/BakeryEditorAssembly.asmdef.meta new file mode 100644 index 00000000..e0e96407 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/BakeryEditorAssembly.asmdef.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 290dd5870d0ead646bcb6ea5c6a60af5 +timeCreated: 1551814754 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftAdditionalConfig.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftAdditionalConfig.cs new file mode 100644 index 00000000..d8d0c427 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftAdditionalConfig.cs @@ -0,0 +1,68 @@ +using UnityEditor; +using UnityEngine; + +public class ftAdditionalConfig +{ + // Affects texture import settings for lightmaps + public const bool mipmapLightmaps = false; + + // Shader eval coeff * gaussian convolution coeff + // ... replaced with more typical convolution coeffs + // Used for legacy light probes + public const float irradianceConvolutionL0 = 0.2820947917f; + public const float irradianceConvolutionL1 = 0.32573500793527993f;//0.4886025119f * 0.7346029443286334f; + public const float irradianceConvolutionL2_4_5_7 = 0.2731371076480198f;//0.29121293321402086f * 1.0925484306f; + public const float irradianceConvolutionL2_6 = 0.07884789131313001f;//0.29121293321402086f * 0.3153915652f; + public const float irradianceConvolutionL2_8 = 0.1365685538240099f;//0.29121293321402086f * 0.5462742153f; + + // Used for L1 light probes and volumes + public const float convL0 = 1; + public const float convL1 = 0.9f; // approx convolution + + // Calculate multiple point lights in one pass. No reason to disable it, unless there is a bug. + public static bool batchPointLights = true; + +#if UNITY_2017_3_OR_NEWER + public const int sectorFarSphereResolution = 256; +#else + // older version can't handle 32 bit meshes + public const int sectorFarSphereResolution = 64; +#endif + +/* + Following settings are moved to Project Settings + (on >= 2018.3; you can also edit BakeryProjectSettings.asset directly) + + // Use PNG instead of TGA for shadowmasks, directions and L1 maps + public const bool preferPNG = false; + + // Padding values for atlas packers + public const int texelPaddingForDefaultAtlasPacker = 3; + public const int texelPaddingForXatlasAtlasPacker = 1; + + // Scales resolution for alpha Meta Pass maps + public const int alphaMetaPassResolutionMultiplier = 2; + + // Render mode for all volumes in the scene. Defaults to Auto, which uses global scene render mode. + public const BakeryLightmapGroup.RenderMode volumeRenderMode = BakeryLightmapGroup.RenderMode.Auto; + + // Should previously rendered Bakery lightmaps be deleted before the new bake? + // Turned off by default because I'm scared of deleting anything + public const bool deletePreviousLightmapsBeforeBake = false; + + // Print information about the bake process to console? + public enum LogLevel + { + Nothing = 0, + Info = 1, // print to Debug.Log + Warning = 2 // print to Debug.LogWarning + } + public const LogLevel logLevel = LogLevel.Info | LogLevel.Warning; + + // Make it work more similar to original Unity behaviour + public const bool alternativeScaleInLightmap = false; + + // Should we adjust sample positions to prevent incorrect shadowing on very low-poly meshes with smooth normals? + public const bool generateSmoothPos = true; +*/ +} diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftAdditionalConfig.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftAdditionalConfig.cs.meta new file mode 100644 index 00000000..c91690d5 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftAdditionalConfig.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1da9342d2a59abd49a8dfb8aa73a87b3 +timeCreated: 1596818791 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildGraphics.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildGraphics.cs new file mode 100644 index 00000000..7e7a1ed2 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildGraphics.cs @@ -0,0 +1,7944 @@ +#if UNITY_EDITOR + +// Disable 'obsolete' warnings +#pragma warning disable 0618 + +using UnityEngine; +using UnityEditor; +using System; +using System.IO; +using System.Text; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEngine.Rendering; +using System.Reflection; + +public class ftBuildGraphics : ScriptableWizard +{ + const int UVGBFLAG_NORMAL = 1; + const int UVGBFLAG_FACENORMAL = 2; + const int UVGBFLAG_ALBEDO = 4; + const int UVGBFLAG_EMISSIVE = 8; + const int UVGBFLAG_POS = 16; + const int UVGBFLAG_SMOOTHPOS = 32; + const int UVGBFLAG_TANGENT = 64; + const int UVGBFLAG_TERRAIN = 128; + const int UVGBFLAG_RESERVED = 256; + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern void InitShaders(); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern void LoadScene(string path); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + private static extern void SetAlbedos(int count, IntPtr[] tex); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + private static extern int CopyAlbedos(); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + private static extern int CopyHeightmapsFromRAM(int count, TexInput[] tex); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern void FreeAlbedoCopies(); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + private static extern void SetAlphas(int count, IntPtr[] tex, float[] alphaRefs, int[] alphaChannels, int numLODs, bool flip); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + private static extern void SetAlphasFromRAM(int count, System.IntPtr tex, float[] alphaRefs, int[] alphaChannels, int numLODs, bool flip); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern void SaveSky(IntPtr tex, float rx, float ry, float rz, float ux, float uy, float uz, float fx, float fy, float fz, string path, bool isLinear, bool doubleLDR); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern void SaveCookie(IntPtr tex, string path); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern void SaveCookieFromRAM(TexInput tex, string path); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern int ftRenderUVGBuffer(); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern void SetUVGBFlags(int flags); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern void SetFixPos(bool enabled); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern void SetCompression(bool enabled); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern int ftGenerateAlphaBuffer(); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern int SaveGBufferMap(IntPtr tex, string path, bool compressed); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern int SaveGBufferMapFromRAM(byte[] tex, int size, string path, bool compressed); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern int GetABGErrorCode(); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern int GetUVGBErrorCode(); + + [DllImport ("uvrepack", CallingConvention=CallingConvention.Cdecl)] + public static extern int uvrLoad(float[] inputUVs, int numVerts, int[] inputIndices, int numIndices); + + [DllImport ("uvrepack", CallingConvention=CallingConvention.Cdecl)] + public static extern int uvrRepack(float padding, int resolution); + + [DllImport ("uvrepack", CallingConvention=CallingConvention.Cdecl)] + public static extern int uvrUnload(); + + public enum TexInputType + { + Color, + FloatColor, + HalfColor + }; + + public struct TexInput + { + public byte[] data; + public ushort width, height; + }; + + static Material matCubemapToStripExport; + + static int voffset, soffset, ioffset; + + static public string scenePath = ""; + + static BufferedBinaryWriterFloat fvbfull, fvbtrace, fvbtraceTex, fvbtraceUV0; + static BufferedBinaryWriterInt fib; + static BinaryWriter fscene, fmesh, flmid, fseamfix, fsurf, fmatid, fmatide, fmatidh, fmatideb, falphaid, fib32, fhmaps; + static BinaryWriter[] fib32lod; + static BinaryWriter[] falphaidlod; + + public static ftLightmapsStorage.ImplicitLightmapData tempStorage = new ftLightmapsStorage.ImplicitLightmapData(); + + public static float texelsPerUnit = 20; + public static int minAutoResolution = 16; + public static int maxAutoResolution = 4096; + public static bool mustBePOT = true; + public static bool autoAtlas = true; + public static bool unwrapUVs = true; + public static bool forceDisableUnwrapUVs = false; + public static bool exportShaderColors = true; // albedo and emission (sometimes normal) always come from the engine now + //public static int atlasPaddingPixels = ftAdditionalConfig.texelPaddingForDefaultAtlasPacker; + public static bool atlasCountPriority = false; + public static bool splitByScene = false; + public static bool uvPaddingMax = false; + public static bool exportTerrainAsHeightmap = true; + public static bool exportTerrainTrees = false; + public static bool uvgbHeightmap = true; + + public static bool texelsPerUnitPerMap = false; + public static float mainLightmapScale = 1; + public static float maskLightmapScale = 1; + public static float dirLightmapScale = 1; + + const float atlasScaleUpValue = 1.01f; + const int atlasMaxTries = 100; + const float alphaInstanceThreshold = 5.0f / 255.0f; + + const bool flipAlpha = true; + + public static string overwriteExtensionCheck = ".hdr"; + public static bool overwriteWarning = false; + public static bool overwriteWarningSelectedOnly = false; + public static bool memoryWarning = false; + public static bool modifyLightmapStorage = true; + public static bool validateLightmapStorageImmutability = true; + public static bool sceneNeedsToBeRebuilt = false; + //public static int unityVersionMajor = 0; + //public static int unityVersionMinor = 0; + + static int areaLightCounter = -2; + public static int sceneLodsUsed = 0; + + static GameObject lmgroupHolder; + static BakeryLightmapGroup lightProbeLMGroup = null; + static BakeryLightmapGroup volumeLMGroup = null; + + static List terrainObjectList; + static List terrainObjectToActual; + static List terrainObjectToHeightMap; + static List terrainObjectToHeightMapRAM; + static List terrainObjectToBounds; + static List terrainObjectToLMID; + static List terrainObjectToBoundsUV; + static List terrainObjectToFlags; + static List> terrainObjectToHeightMips; + //static List> terrainObjectToNormalMips; + //static List terrainObjectToNormalMip0; + static List temporaryAreaLightMeshList; + static List temporaryAreaLightMeshList2; + public static List temporaryGameObjects; + + static Dictionary cmp_objToLodLevel; + static Dictionary cmp_holderObjArea; + + public static Dictionary> lodLevelsVisibleInLodLevel = new Dictionary>(); // defines LOD levels visible in chosen LOD level + + public static List vbtraceTexPosNormalArray; // global vbTraceTex.bin positions/normals + public static List vbtraceTexUVArray; // global vbTraceTex.bin UVs + public static float[] vbtraceTexUVArrayLOD; // global vbTraceTex.bin LOD UVs + + public static List atlasOnlyObj; + public static List atlasOnlyScaleOffset; + public static List atlasOnlySize; + public static List atlasOnlyID; + + public static ftGlobalStorage.AtlasPacker atlasPacker = ftGlobalStorage.AtlasPacker.xatlas; + + public static bool forceAllAreaLightsSelfshadow = false; + + public static bool postPacking = true; + public static bool holeFilling = false; + + static int floatOverWarnCount = 0; + const int maxFloatOverWarn = 10; + + static ftGlobalStorage gstorage; + static BakeryProjectSettings pstorage; + + static public void DebugLogError(string text) + { + ftRenderLightmap.DebugLogError(text); + } + + class FarSphereRenderData + { + public RenderTexture[] albedo, depth, normal; + public Matrix4x4[] viewProj; + public Vector3 pos; + public Mesh[] meshes; + public Texture2D[] textures; + } + + static Shader farSphereRshader, farSphereRshaderOcc, farSphereSshader, farSphereProjClipShader, farSphereDilateShader; + static Material farSphereMat, farSphereMatOcc, farSphereProjClipMat, farSphereDilateMat; + static ComputeShader farSphereCSTransform, farSphereCSCull; + + class AtlasNode + { + public AtlasNode child0, child1; + public Rect rc; + public GameObject obj; + bool leaf = true; + + public AtlasNode Insert(GameObject o, Rect rect) + { + if (!leaf) + { + var newNode = child0.Insert(o, rect); + if (newNode != null) return newNode; + + return child1.Insert(o, rect); + } + else + { + if (obj != null) return null; + + var fits = (rect.width <= rc.width && rect.height <= rc.height); + if (!fits) return null; + + var fitsExactly = (rect.width == rc.width && rect.height == rc.height); + if (fitsExactly) + { + obj = o; + return this; + } + + child0 = new AtlasNode(); + child1 = new AtlasNode(); + + var dw = rc.width - rect.width; + var dh = rc.height - rect.height; + + if (dw > dh) + { + child0.rc = new Rect(rc.x, rc.y, rect.width, rc.height); + child1.rc = new Rect(rc.x + rect.width, rc.y, rc.width - rect.width, rc.height); + } + else + { + child0.rc = new Rect(rc.x, rc.y, rc.width, rect.height); + child1.rc = new Rect(rc.x, rc.y + rect.height, rc.width, rc.height - rect.height); + } + leaf = false; + + return child0.Insert(o, rect); + } + } + + public void GetMax(ref float maxx, ref float maxy) + { + if (obj != null) + { + if ((rc.x + rc.width) > maxx) maxx = rc.x + rc.width; + if ((rc.y + rc.height) > maxy) maxy = rc.y + rc.height; + } + if (child0 != null) child0.GetMax(ref maxx, ref maxy); + if (child1 != null) child1.GetMax(ref maxx, ref maxy); + } + + public void Transform(float offsetx, float offsety, float scalex, float scaley) + { + rc.x *= scalex; + rc.y *= scaley; + rc.x += offsetx; + rc.y += offsety; + rc.width *= scalex; + rc.height *= scaley; + if (child0 != null) child0.Transform(offsetx, offsety, scalex, scaley); + if (child1 != null) child1.Transform(offsetx, offsety, scalex, scaley); + } + }; + + static ftBuildGraphics() + { + //ftRenderLightmap.PatchPath(); + //var unityVer = Application.unityVersion.Split('.'); + //unityVersionMajor = Int32.Parse(unityVer[0]); + //unityVersionMinor = Int32.Parse(unityVer[1]); + } + + static void DebugLogInfo(string info) + { + if (pstorage == null) pstorage = ftLightmaps.GetProjectSettings(); + if ((pstorage.logLevel & (int)BakeryProjectSettings.LogLevel.Info) != 0) Debug.Log(info); + } + + static void DebugLogWarning(string info) + { + if (pstorage == null) pstorage = ftLightmaps.GetProjectSettings(); + if ((pstorage.logLevel & (int)BakeryProjectSettings.LogLevel.Warning) != 0) Debug.LogWarning(info); + } + + static void exportVBPos(BinaryWriter f, Transform t, Mesh m, Vector3[] vertices) + { + for(int i=0;i(); + if (mr as MeshRenderer == null && mr as SkinnedMeshRenderer == null) + { + // possibly multiple renderers on one gameobject? + mr = obj.GetComponent() as Renderer; + if (mr != null) return mr; + mr = obj.GetComponent() as Renderer; + if (mr != null) return mr; + return null; + } + return mr; + } + + static BakeryLightmapGroup GetLMGroupFromObjectExplicit(GameObject obj, ExportSceneData data) + { + lmgroupHolder = null; + var lmgroupSelector = obj.GetComponent(); // if object has explicit lmgroup + if (lmgroupSelector == null) + { + // if parents have explicit lmgroup + var t2 = obj.transform.parent; + while(lmgroupSelector == null && t2 != null) + { + lmgroupSelector = t2.GetComponent(); + t2 = t2.parent; + } + } + BakeryLightmapGroup lmgroup = null; + if (lmgroupSelector != null) + { + lmgroup = lmgroupSelector.lmgroupAsset as BakeryLightmapGroup; + lmgroupHolder = lmgroupSelector.gameObject; + + //var so = new SerializedObject(obj.GetComponent()); + //var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue; + var scaleInLm = data.objToScaleInLm[obj]; + if (scaleInLm == 0.0f) lmgroup = data.autoVertexGroup; + //null; // ignore lightmaps when scaleInLightmap == 0 + } + return lmgroup; + } + + static BakeryLightmapGroup GetLMGroupFromObject(GameObject obj, ExportSceneData data) + { + UnityEngine.Object lmgroupObj = null; + BakeryLightmapGroup lmgroup = null; + lmgroupHolder = null; + + var lmgroupSelector = obj.GetComponent(); // if object has explicit lmgroup + tempStorage.implicitGroupMap.TryGetValue(obj, out lmgroupObj); // or implicit + lmgroup = (BakeryLightmapGroup)lmgroupObj; + lmgroupHolder = obj; + if (lmgroupSelector == null && lmgroup == null) + { + // if parents have explicit/implicit lmgroup + var t2 = obj.transform.parent; + while(lmgroupSelector == null && lmgroup == null && t2 != null) + { + lmgroupSelector = t2.GetComponent(); + tempStorage.implicitGroupMap.TryGetValue(t2.gameObject, out lmgroupObj); + lmgroup = (BakeryLightmapGroup)lmgroupObj; + lmgroupHolder = t2.gameObject; + t2 = t2.parent; + } + } + if (lmgroupSelector != null) + { + lmgroup = lmgroupSelector.lmgroupAsset as BakeryLightmapGroup; + } + + if (lmgroup != null) + { + var r = GetValidRenderer(obj); + if (r) + { + //var so = new SerializedObject(r); + //var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue; + var scaleInLm = data.objToScaleInLm[obj]; + if (scaleInLm == 0.0f) lmgroup = data.autoVertexGroup; + // null; // ignore lightmaps when scaleInLightmap == 0 + } + } + else + { + lmgroupHolder = null; + } + + return lmgroup; + } + + // use by ftRenderLightmap + public static BakeryLightmapGroup GetLMGroupFromObject(GameObject obj) + { + UnityEngine.Object lmgroupObj = null; + BakeryLightmapGroup lmgroup = null; + lmgroupHolder = null; + + var lmgroupSelector = obj.GetComponent(); // if object has explicit lmgroup + tempStorage.implicitGroupMap.TryGetValue(obj, out lmgroupObj); // or implicit + lmgroup = (BakeryLightmapGroup)lmgroupObj; + lmgroupHolder = obj; + if (lmgroupSelector == null && lmgroup == null) + { + // if parents have explicit/implicit lmgroup + var t2 = obj.transform.parent; + while(lmgroupSelector == null && lmgroup == null && t2 != null) + { + lmgroupSelector = t2.GetComponent(); + tempStorage.implicitGroupMap.TryGetValue(t2.gameObject, out lmgroupObj); + lmgroup = (BakeryLightmapGroup)lmgroupObj; + lmgroupHolder = t2.gameObject; + t2 = t2.parent; + } + } + if (lmgroupSelector != null) + { + lmgroup = lmgroupSelector.lmgroupAsset as BakeryLightmapGroup; + } + + if (lmgroup != null) + { + var r = GetValidRenderer(obj); + if (r) + { + var so = new SerializedObject(r); + var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue; + //var scaleInLm = data.objToScaleInLm[obj]; + if (scaleInLm == 0.0f) lmgroup = null; + // null; // ignore lightmaps when scaleInLightmap == 0 + } + } + else + { + lmgroupHolder = null; + } + + return lmgroup; + } + + public static void exportVBTraceTexAttribs(List arrPosNormal, List arrUV, + Vector3[] vertices, Vector3[] normals, Vector2[] uv2, int lmid, bool vertexBake, GameObject obj) + { + for(int i=0;i0) + { + u = Mathf.Clamp(uv2[i].x, 0, 0.99999f); + v = Mathf.Clamp(1.0f - uv2[i].y, 0, 0.99999f); + } + } + else + { + if (uv2.Length>0 && !vertexBake) + { + u = Mathf.Clamp(uv2[i].x, 0, 0.99999f); + v = Mathf.Clamp(uv2[i].y, 0, 0.99999f); + } + else if (vertexBake) + { + u = uv2[i].x; + v = uv2[i].y - 1.0f; + } + } + + float origU = u; + if (lmid >= 0) + { + u += lmid * 10; + if ((int)u > lmid*10) + { + // attempt fixing float overflow + u = (lmid*10+1) - (lmid*10+1)*0.0000002f; + if ((int)u > lmid*10) + { + if (floatOverWarnCount < maxFloatOverWarn) + { + Debug.LogError("Float overflow for " + obj.name + " (U: " + origU + ", LMID: " + lmid + ")"); + floatOverWarnCount++; + } + } + } + } + else + { + u = lmid * 10 - u; + if ((int)u != lmid*10) + { + u = -u; + lmid = -lmid; + u = (lmid*10+1) - (lmid*10+1)*0.0000002f; + if ((int)u > lmid*10) + { + if (floatOverWarnCount < maxFloatOverWarn) + { + Debug.LogError("Float overflow for " + obj.name + " (U: " + origU + ", LMID: " + lmid + ")"); + floatOverWarnCount++; + } + } + u = -u; + lmid = -lmid; + } + } + + arrUV.Add(u); + arrUV.Add(v); + } + } + + static void exportVBTraceUV0(BufferedBinaryWriterFloat f, Vector2[] uvs, int vertCount) + { + if (uvs.Length == 0) + { + for(int i=0;i0) + { + f.Write(uv2[i].x); + f.Write(uv2[i].y); + } + else + { + f.Write(0.0f); + f.Write(0.0f); + } + } + } + + // Either I'm dumb, or it's impossible to make generics work with it (only worked in .NET 3.5) + class BufferedBinaryWriterFloat + { + [StructLayout(LayoutKind.Explicit)] + public class ReinterpretBuffer + { + [FieldOffset(0)] + public byte[] bytes; + [FieldOffset(0)] + public float[] elements; + } + + BinaryWriter f; + ReinterpretBuffer buffer; + int bufferPtr; + int bufferSize; + int elementSize; + + public BufferedBinaryWriterFloat(BinaryWriter b, int elemSize = 4, int buffSizeInFloats = 1024*1024) + { + f = b; + buffer = new ReinterpretBuffer(); + buffer.bytes = new byte[buffSizeInFloats * elemSize]; + bufferPtr = 0; + bufferSize = buffSizeInFloats; + elementSize = elemSize; + } + + void Flush() + { + if (bufferPtr == 0) return; + f.Write(buffer.bytes, 0, bufferPtr * elementSize); + bufferPtr = 0; + } + + public void Write(float x) + { + buffer.elements[bufferPtr] = x; + bufferPtr++; + if (bufferPtr == bufferSize) Flush(); + } + + public void Close() + { + Flush(); + f.Close(); + } + } + class BufferedBinaryWriterInt + { + [StructLayout(LayoutKind.Explicit)] + public class ReinterpretBuffer + { + [FieldOffset(0)] + public byte[] bytes; + [FieldOffset(0)] + public int[] elements; + } + + BinaryWriter f; + ReinterpretBuffer buffer; + int bufferPtr; + int bufferSize; + int elementSize; + + public BufferedBinaryWriterInt(BinaryWriter b, int elemSize = 4, int buffSizeInFloats = 1024*1024) + { + f = b; + buffer = new ReinterpretBuffer(); + buffer.bytes = new byte[buffSizeInFloats * elemSize]; + bufferPtr = 0; + bufferSize = buffSizeInFloats; + elementSize = elemSize; + } + + void Flush() + { + if (bufferPtr == 0) return; + f.Write(buffer.bytes, 0, bufferPtr * elementSize); + bufferPtr = 0; + } + + public void Write(int x) + { + buffer.elements[bufferPtr] = x; + bufferPtr++; + if (bufferPtr == bufferSize) Flush(); + } + + public void Close() + { + Flush(); + f.Close(); + } + } + + static void exportVBFull(BufferedBinaryWriterFloat f, Vector3[] vertices, Vector3[] normals, Vector4[] tangents, Vector2[] uv, Vector2[] uv2) + { + bool hasUV = uv.Length > 0; + bool hasUV2 = uv2.Length > 0; + + for(int i=0;i indicesOpaque, List indicesTransparent, List indicesLMID, + int[] indices, bool isFlipped, int offset, int indexOffsetLMID, BinaryWriter falphaid, + ushort alphaID) + { + //var indices = m.GetTriangles(i); + var indicesOut = alphaID == 0xFFFF ? indicesOpaque : indicesTransparent; + + int indexA, indexB, indexC; + + for(int j=0;j(); + if (areaLight == null) + { + int index = temporaryAreaLightMeshList.IndexOf(obj); + if (index >= 0) + { + areaLight = temporaryAreaLightMeshList2[index]; + } + } + if (areaLight != null) + { + f.Write(areaLightCounter); + areaLight.lmid = areaLightCounter; + areaLightCounter--; + return areaLightCounter; + } + else if (lmgroup != null) + { + f.Write(lmgroup.id); + return lmgroup.id; + } + else + { + f.Write(0xFFFFFFFF); + return -1; + } + } + + static Vector2[] GenerateVertexBakeUVs(int voffset, int vlength, int totalVertexCount) + { + int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)totalVertexCount)); + atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)ftRenderLightmap.tileSize) * ftRenderLightmap.tileSize; + var uvs = new Vector2[vlength]; + float mul = 1.0f / atlasTexSize; + float add = mul * 0.5f; + for(int i=0; i(); + return mrSkin != null ? mrSkin.sharedMesh : (mf != null ? mf.sharedMesh : null); + } + + public static Mesh GetSharedMeshBaked(GameObject obj) + { + var mrSkin = obj.GetComponent(); + if (mrSkin != null) + { + var baked = new Mesh(); + mrSkin.BakeMesh(baked); + return baked; + } + var mf = obj.GetComponent(); + return (mf != null ? mf.sharedMesh : null); + } + + public static Mesh GetSharedMesh(GameObject obj) + { + var mrSkin = obj.GetComponent(); + var mf = obj.GetComponent(); + return mrSkin != null ? mrSkin.sharedMesh : (mf != null ? mf.sharedMesh : null); + } + + public static Mesh GetSharedMeshSkinned(GameObject obj, out bool isSkin) + { + var mrSkin = obj.GetComponent(); + Mesh mesh; + if (mrSkin != null) + { + mesh = new Mesh(); + mrSkin.BakeMesh(mesh); + isSkin = mrSkin.bones.Length > 0; // blendshape-only don't need scale? + } + else + { + isSkin = false; + var mf = obj.GetComponent(); + if (mf == null) return null; + mesh = mf.sharedMesh; + } + return mesh; + } + + static GameObject TestPackAsSingleSquare(GameObject holder) + { + var t = holder.transform; + var p = t.parent; + while(p != null) + { + if (p.GetComponent() != null) return p.gameObject; + p = p.parent; + } + return holder; + } + + static bool ModelUVsOverlap(ModelImporter importer, ftGlobalStorage store) + { + if (importer.generateSecondaryUV) return true; + + var path = importer.assetPath; + /*for(int i=0; i= 0 && index < store.uvOverlapAssetList.Count) + { + if (store.uvOverlapAssetList[index] == 0) + { + return false; + } + else + { + return true; + } + } + + var newAsset = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; + ftModelPostProcessor.CheckUVOverlap(newAsset, path); + + /*for(int i=0; i= 0) + { + if (store.uvOverlapAssetList[index] == 0) + { + return false; + } + else + { + return true; + } + } + + return true; + } + + static bool NeedsTangents(BakeryLightmapGroup lmgroup, bool tangentSHLights) + { + // RNM requires tangents + if (ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.RNM || + (lmgroup!=null && lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.RNM)) return true; + + // SH requires tangents only if there is a SH skylight + if (!tangentSHLights) return false; + + if (ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.SH || + (lmgroup!=null && lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.SH)) return true; + + return false; + } + + static long GetTime() + { + return System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; + } + + static public string progressBarText; + static public float progressBarPercent = 0; + static public bool userCanceled = false; + //static IEnumerator progressFunc; + static EditorWindow activeWindow; + static void ProgressBarInit(string startText, EditorWindow window = null) + { + progressBarText = startText; + if (ftRenderLightmap.showProgressBar) ftRenderLightmap.simpleProgressBarShow("Bakery", progressBarText, progressBarPercent, 0, false); + } + static void ProgressBarShow(string text, float percent, bool onTop) + { + progressBarText = text; + progressBarPercent = percent; + if (ftRenderLightmap.showProgressBar) ftRenderLightmap.simpleProgressBarShow("Bakery", progressBarText, progressBarPercent, 0, onTop); + userCanceled = ftRenderLightmap.simpleProgressBarCancelled(); + } + + public static void FreeTemporaryAreaLightMeshes() + { + if (temporaryAreaLightMeshList != null) + { + for(int i=0; i(); + //if (mr != null) DestroyImmediate(mr); + //var mf = temporaryAreaLightMeshList[i].GetComponent(); + //if (mf != null) DestroyImmediate(mf); + DestroyImmediate(temporaryAreaLightMeshList[i]); + } + } + temporaryAreaLightMeshList = null; + } + } + + public static void ProgressBarEnd(bool removeTempObjects)// bool isError = true) + { + if (removeTempObjects) + { + if (terrainObjectList != null) + { + for(int i=0; i groupList) + { + id = id < 0 ? -1 : id; + if (lmgroup != null && lmgroup.parentName != null && lmgroup.parentName.Length > 0 && lmgroup.parentName != "|") + { + for(int g=0; g(); + if (storages[i] == null) + { + storages[i] = gg.AddComponent(); + } + + if (modifyLightmapStorage) + { + /* + storages[i].bakedRenderers = new List(); + storages[i].bakedIDs = new List(); + storages[i].bakedScaleOffset = new List(); + storages[i].bakedVertexOffset = new List(); + storages[i].bakedVertexColorMesh = new List(); + storages[i].bakedRenderersTerrain = new List(); + storages[i].bakedIDsTerrain = new List(); + storages[i].bakedScaleOffsetTerrain = new List(); + */ + storages[i].hasEmissive = new List(); + storages[i].lmGroupLODResFlags = null; + storages[i].lmGroupMinLOD = null; + storages[i].lmGroupLODMatrix = null; + storages[i].nonBakedRenderers = new List(); + } + if (first) + { + data.firstNonNullStorage = i; + first = false; + } + storages[i].implicitGroups = new List(); + storages[i].implicitGroupedObjects = new List(); + sceneToID[scene] = i; + } + + //var go = GameObject.Find("!ftraceLightmaps"); + //data.settingsStorage = go.GetComponent(); + } + + static void InitSceneStorage2(ExportSceneData data) + { + var storages = data.storages; + for(int i=0; i(); + storages[i].bakedIDs = new List(); + storages[i].bakedScaleOffset = new List(); + storages[i].bakedVertexOffset = new List(); + storages[i].bakedVertexColorMesh = new List(); + storages[i].bakedRenderersTerrain = new List(); + storages[i].bakedIDsTerrain = new List(); + storages[i].bakedScaleOffsetTerrain = new List(); + } + } + } + + // Used to pass maps to alphaBufferGen/frender via RAM (non-DX11) + public static TexInput InputDataFromTex(Texture tex, TexInputType ttype = TexInputType.Color) + { + RenderTextureFormat rtFormat = RenderTextureFormat.ARGB32; + TextureFormat texFormat = TextureFormat.RGBA32; + if (ttype == TexInputType.FloatColor) + { + rtFormat = RenderTextureFormat.ARGBFloat; + texFormat = TextureFormat.RGBAFloat; + } + else if (ttype == TexInputType.HalfColor) + { + rtFormat = RenderTextureFormat.ARGBHalf; + texFormat = TextureFormat.RGBAHalf; + } + var rt = new RenderTexture(tex.width, tex.height, 0, rtFormat); + var readableTex = new Texture2D(tex.width, tex.height, texFormat, false); + Graphics.Blit(tex, rt); + Graphics.SetRenderTarget(rt); + readableTex.ReadPixels(new Rect(0,0,tex.width,tex.height), 0, 0, false); + readableTex.Apply(); + var bytes = readableTex.GetRawTextureData(); + + var a = new TexInput(); + a.data = bytes; + a.width = (ushort)tex.width; + a.height = (ushort)tex.height; + + DestroyImmediate(readableTex); + rt.Release(); + + return a; + } + + public static byte[] InputBytesFromTex(Texture tex, TexInputType ttype = TexInputType.Color) + { + RenderTextureFormat rtFormat = RenderTextureFormat.ARGB32; + TextureFormat texFormat = TextureFormat.RGBA32; + byte[] header = ftDDS.ddsHeaderRGBA8; + if (ttype == TexInputType.FloatColor) + { + rtFormat = RenderTextureFormat.ARGBFloat; + texFormat = TextureFormat.RGBAFloat; + header = ftDDS.ddsHeaderFloat4; + } + else if (ttype == TexInputType.HalfColor) + { + rtFormat = RenderTextureFormat.ARGBHalf; + texFormat = TextureFormat.RGBAHalf; + header = ftDDS.ddsHeaderHalf4; + } + var rt = new RenderTexture(tex.width, tex.height, 0, rtFormat); + var readableTex = new Texture2D(tex.width, tex.height, texFormat, false); + Graphics.Blit(tex, rt); + Graphics.SetRenderTarget(rt); + readableTex.ReadPixels(new Rect(0,0,tex.width,tex.height), 0, 0, false); + readableTex.Apply(); + var bytes = readableTex.GetRawTextureData(); + + var fbytes = new byte[128 + bytes.Length]; + System.Buffer.BlockCopy(header, 0, fbytes, 0, 128); + System.Buffer.BlockCopy(BitConverter.GetBytes(tex.height), 0, fbytes, 12, 4); + System.Buffer.BlockCopy(BitConverter.GetBytes(tex.width), 0, fbytes, 16, 4); + System.Buffer.BlockCopy(bytes, 0, fbytes, 128, bytes.Length); + + DestroyImmediate(readableTex); + rt.Release(); + + return fbytes; + } + + public static TexInput InputDataFromCubemap(Texture tex, Matrix4x4 mtx, bool isLinear, bool isDoubleLDR, TexInputType ttype = TexInputType.Color) + { + RenderTextureFormat rtFormat = RenderTextureFormat.ARGB32; + TextureFormat texFormat = TextureFormat.RGBA32; + if (ttype == TexInputType.FloatColor) + { + rtFormat = RenderTextureFormat.ARGBFloat; + texFormat = TextureFormat.RGBAFloat; + } + else if (ttype == TexInputType.HalfColor) + { + rtFormat = RenderTextureFormat.ARGBHalf; + texFormat = TextureFormat.RGBAHalf; + } + + int outWidth = System.Math.Min(tex.width, 512); + int outHeight = System.Math.Min(tex.height, 512); + + var rt = new RenderTexture(outWidth, outHeight * 6, 0, rtFormat); + var readableTex = new Texture2D(outWidth, outHeight * 6, texFormat, false); + + if (matCubemapToStripExport == null) matCubemapToStripExport = new Material(Shader.Find("Hidden/ftCubemap2StripExport")); + matCubemapToStripExport.SetMatrix("transform", mtx); + matCubemapToStripExport.SetVector("isLinear_isDoubleLDR", new Vector4(isLinear ? 1 : 0, isDoubleLDR ? 1 : 0, 0, 0)); + + Graphics.Blit(tex, rt, matCubemapToStripExport); + Graphics.SetRenderTarget(rt); + readableTex.ReadPixels(new Rect(0,0,outWidth, outHeight * 6), 0, 0, false); + readableTex.Apply(); + var bytes = readableTex.GetRawTextureData(); + + var a = new TexInput(); + a.data = bytes; + a.width = (ushort)outWidth; + a.height = (ushort)(outHeight * 6); + + DestroyImmediate(readableTex); + rt.Release(); + + return a; + } + + static IEnumerator CreateLightProbeLMGroup(ExportSceneData data) + { + var storages = data.storages; + var sceneToID = data.sceneToID; + var lmBounds = data.lmBounds; + var groupList = data.groupList; + + var probes = LightmapSettings.lightProbes; + if (probes == null) + { + DebugLogError("No probes in LightingDataAsset"); + yield break; + } + var positions = probes.positions; + + int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)probes.count)); + atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)ftRenderLightmap.tileSize) * ftRenderLightmap.tileSize; + var uvpos = new float[atlasTexSize * atlasTexSize * 4]; + var uvnormal = new byte[atlasTexSize * atlasTexSize * 4]; + + for(int i=0; i(); + lightProbeLMGroup.name = "probes"; + lightProbeLMGroup.probes = true; + lightProbeLMGroup.isImplicit = true; + lightProbeLMGroup.resolution = 256; + lightProbeLMGroup.bitmask = 1; + lightProbeLMGroup.mode = BakeryLightmapGroup.ftLMGroupMode.Vertex; + lightProbeLMGroup.id = data.lmid; + lightProbeLMGroup.totalVertexCount = probes.count; + lightProbeLMGroup.vertexCounter = 0; + lightProbeLMGroup.renderDirMode = BakeryLightmapGroup.RenderDirMode.ProbeSH; + lightProbeLMGroup.renderMode = (ftRenderLightmap.instance.userRenderMode == ftRenderLightmap.RenderMode.Subtractive && ftRenderLightmap.useUnityForOcclsusionProbes) ? BakeryLightmapGroup.RenderMode.Indirect : BakeryLightmapGroup.RenderMode.Auto; + groupList.Add(lightProbeLMGroup); + lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(10000,10000,10000))); + data.lmid++; + + storages[sceneToID[EditorSceneManager.GetActiveScene()]].implicitGroups.Add(lightProbeLMGroup); + storages[sceneToID[EditorSceneManager.GetActiveScene()]].implicitGroupedObjects.Add(null); + } + + static IEnumerator CreateVolumeLMGroup(ExportSceneData data) + { + ftRenderLightmap.hasAnyVolumes = false; + + var vols = ftRenderLightmap.FindBakeableVolumes(); + if (vols.Length == 0) yield break; + + ftRenderLightmap.hasAnyVolumes = true; + + var storages = data.storages; + var sceneToID = data.sceneToID; + var lmBounds = data.lmBounds; + var groupList = data.groupList; + + int numTotalProbes = 0; + for(int v=0; v(); + volumeLMGroup.name = "volumes"; + volumeLMGroup.probes = true; + volumeLMGroup.fixPos3D = true; + volumeLMGroup.voxelSize = halfVoxelSize * 2; // incorrect... should be different for every probe + volumeLMGroup.isImplicit = true; + volumeLMGroup.resolution = 256; + volumeLMGroup.bitmask = 1; + volumeLMGroup.mode = BakeryLightmapGroup.ftLMGroupMode.Vertex; + volumeLMGroup.id = data.lmid; + volumeLMGroup.totalVertexCount = numTotalProbes; + volumeLMGroup.vertexCounter = 0; + volumeLMGroup.renderMode = (BakeryLightmapGroup.RenderMode)pstorage.volumeRenderMode; + volumeLMGroup.renderDirMode = BakeryLightmapGroup.RenderDirMode.ProbeSH; + groupList.Add(volumeLMGroup); + lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(10000,10000,10000))); + data.lmid++; + + storages[sceneToID[EditorSceneManager.GetActiveScene()]].implicitGroups.Add(volumeLMGroup); + storages[sceneToID[EditorSceneManager.GetActiveScene()]].implicitGroupedObjects.Add(null); + } + + static void CollectExplicitLMGroups(ExportSceneData data) + { + var groupList = data.groupList; + var lmBounds = data.lmBounds; + + // Find explicit LMGroups + // (Also init lmBounds and LMID) + var groupSelectors = new List(FindObjectsOfType(typeof(BakeryLightmapGroupSelector)) as BakeryLightmapGroupSelector[]); + for(int i=0; i(); + if (terr == null) return; + if (!terr.enabled) return; + + if (!obj.activeInHierarchy) return; + if ((obj.hideFlags & (HideFlags.DontSave|HideFlags.HideAndDontSave)) != 0) return; // skip temp objects + if (obj.tag == "EditorOnly") return; // skip temp objects + if ((GameObjectUtility.GetStaticEditorFlags(obj) & StaticEditorFlags.LightmapStatic) == 0) return; // skip dynamic + + var so = new SerializedObject(terr); + var scaleInLmTerr = so.FindProperty("m_ScaleInLightmap").floatValue; + + var terrParent = new GameObject(); + SceneManager.MoveGameObjectToScene(terrParent, obj.scene); + terrParent.transform.parent = obj.transform;//.parent; + var expGroup = obj.GetComponent(); + if (expGroup != null) + { + var expGroup2 = terrParent.AddComponent(); + expGroup2.lmgroupAsset = expGroup.lmgroupAsset; + expGroup2.instanceResolutionOverride = expGroup.instanceResolutionOverride; + expGroup2.instanceResolution = expGroup.instanceResolution; + } + terrParent.name = "__ExportTerrainParent"; + terrainObjectList.Add(terrParent); + terrainObjectToActual.Add(terr); + + var tdata = terr.terrainData; + int res = tdata.heightmapResolution; + var heightmap = tdata.GetHeights(0, 0, res, res); + var uvscale = new Vector2(1,1) / (res-1); + var uvoffset = new Vector2(0,0); + var gposOffset = obj.transform.position; + float scaleX = tdata.size.x / (res-1); + float scaleY = tdata.size.y; + float scaleZ = tdata.size.z / (res-1); + + int patchRes = res; + while(patchRes > 254) patchRes = 254;//patchRes /= 2; + int numVerts = patchRes * patchRes; + int numPatches = (int)Mathf.Ceil(res / (float)patchRes); + + // Gen terrain texture + var oldMat = terr.materialTemplate; + var oldMatType = terr.materialType; + var oldPos = obj.transform.position; +#if UNITY_2018_3_OR_NEWER + var oldInstanced = terr.drawInstanced; +#endif + var unlitTerrainMat = new Material(Shader.Find("Hidden/ftUnlitTerrain")); + //unlitTerrainMat = AssetDatabase.LoadAssetAtPath("Assets/Bakery/ftUnlitTerrain.mat", typeof(Material)) as Material; + terr.materialTemplate = unlitTerrainMat; + terr.materialType = Terrain.MaterialType.Custom; +#if UNITY_2018_3_OR_NEWER + terr.drawInstanced = false; +#endif + obj.transform.position = new Vector3(-10000, -10000, -10000); // let's hope it's not the worst idea + var tempCamGO = new GameObject(); + tempCamGO.transform.parent = obj.transform; + tempCamGO.transform.localPosition = new Vector3(tdata.size.x * 0.5f, scaleY + 1, tdata.size.z * 0.5f); + tempCamGO.transform.eulerAngles = new Vector3(90,0,0); + var tempCam = tempCamGO.AddComponent(); + tempCam.orthographic = true; + tempCam.orthographicSize = Mathf.Max(tdata.size.x, tdata.size.z) * 0.5f; + tempCam.aspect = Mathf.Max(tdata.size.x, tdata.size.z) / Mathf.Min(tdata.size.x, tdata.size.z); + tempCam.enabled = false; + tempCam.clearFlags = CameraClearFlags.SolidColor; + tempCam.backgroundColor = new Color(0,0,0,0); + tempCam.targetTexture = + new RenderTexture(tdata.baseMapResolution, tdata.baseMapResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); + var tex = new Texture2D(tdata.baseMapResolution, tdata.baseMapResolution, TextureFormat.ARGB32, true, false); + RenderTexture.active = tempCam.targetTexture; + tempCam.Render(); + terr.materialTemplate = oldMat; + terr.materialType = oldMatType; +#if UNITY_2018_3_OR_NEWER + terr.drawInstanced = oldInstanced; +#endif + obj.transform.position = oldPos; + RenderTexture.active = tempCam.targetTexture; + tex.ReadPixels(new Rect(0,0,tdata.baseMapResolution, tdata.baseMapResolution), 0, 0, true); + tex.Apply(); + unlitTerrainMat.mainTexture = tex; + Graphics.SetRenderTarget(null); + DestroyImmediate(tempCamGO); + + bool isDX11 = SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11; + + if (exportTerrainAsHeightmap) + { + var hmap = new BinaryWriter(File.Open(scenePath + "/height" + terrainObjectToHeightMap.Count + ".dds", FileMode.Create)); + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add("height" + terrainObjectToHeightMap.Count + ".dds"); + + hmap.Write(ftDDS.ddsHeaderR32F); + var bytes = new byte[res * res * 4]; + + // Normalize heights + float maxHeight = 0; + float height; + for(int y=0; y maxHeight) maxHeight = height; + } + } + maxHeight = Mathf.Max(maxHeight, 0.0001f); + float invMaxHeight = 1.0f / maxHeight; + for(int y=0; y()); + //terrainObjectToNormalMips.Add(new List()); + + if (mipRes > 0) + { + floats = new float[mipRes * mipRes]; + //normals = new Vector3[mipRes * mipRes]; + for(int y=0; y h10 ? h00 : h10; + height = height > h01 ? height : h01; + height = height > h11 ? height : h11; + floats[y*mipRes+x] = height;*/ + + float maxVal = 0; + for(int yy=0; yy<3; yy++) + { + for(int xx=0; xx<3; xx++) + { + float val = heightmap[y*2+yy, x*2+xx]; + if (val > maxVal) maxVal = val; + } + } + floats[y*mipRes+x] = maxVal; + + //n00 = normalsPrev[y*2 * res + x*2]; + //n10 = normalsPrev[y*2 * res + x*2+1]; + //n01 = normalsPrev[(y*2+1) * res + x*2]; + //n11 = normalsPrev[(y*2+1) * res + x*2+1]; + //normals[y*mipRes+x] = (n00 + n10 + n01 + n11); + } + } + + System.Buffer.BlockCopy(floats, 0, bytes, 0, mipRes*mipRes*4); + hmap.Write(bytes, 0, mipRes*mipRes*4); + + float[] storedMip = new float[mipRes*mipRes]; + System.Buffer.BlockCopy(floats, 0, storedMip, 0, mipRes*mipRes*4); + terrainObjectToHeightMips[terrIndex].Add(storedMip); + //terrainObjectToNormalMips[terrIndex].Add(normals); + + mipCount++; + mipRes /= 2; + } + + // Next mips are regular max() of 4 texels + while(mipRes > 1) + { + if (floatsPrev == null) + { + floatsPrev = floats; + floats = new float[mipRes * mipRes]; + } + + //normalsPrev = normals; + //normals = new Vector3[mipRes * mipRes]; + + for(int y=0; y h10 ? h00 : h10; + height = height > h01 ? height : h01; + height = height > h11 ? height : h11; + floats[y*mipRes+x] = height; + + //n00 = normalsPrev[y*2 * mipRes*2 + x*2]; + //n10 = normalsPrev[y*2 * mipRes*2 + x*2+1]; + //n01 = normalsPrev[(y*2+1) * mipRes*2 + x*2]; + //n11 = normalsPrev[(y*2+1) * mipRes*2 + x*2+1]; + //normals[y*mipRes+x] = (n00 + n10 + n01 + n11); + } + } + + System.Buffer.BlockCopy(floats, 0, bytes, 0, mipRes*mipRes*4); + hmap.Write(bytes, 0, mipRes*mipRes*4); + + float[] storedMip = new float[mipRes*mipRes]; + System.Buffer.BlockCopy(floats, 0, storedMip, 0, mipRes*mipRes*4); + terrainObjectToHeightMips[terrIndex].Add(storedMip); + //terrainObjectToNormalMips[terrIndex].Add(normals); + + mipCount++; + mipRes /= 2; + + floatsTmp = floatsPrev; + floatsPrev = floats; + floats = floatsTmp; + } + + hmap.BaseStream.Seek(12, SeekOrigin.Begin); + hmap.Write(res); + hmap.Write(res); + hmap.BaseStream.Seek(28, SeekOrigin.Begin); + hmap.Write(mipCount); + hmap.Close(); + + // Create dummy plane for packing/albedo/emissive purposes + var mesh = new Mesh(); + mesh.vertices = new Vector3[] { gposOffset, + gposOffset + new Vector3(tdata.size.x, 0, 0), + gposOffset + new Vector3(tdata.size.x, 0, tdata.size.z), + gposOffset + new Vector3(0, 0, tdata.size.z) }; + mesh.triangles = new int[]{0,1,2, 2,3,0}; + mesh.normals = new Vector3[]{Vector3.up, Vector3.up, Vector3.up, Vector3.up}; + mesh.uv = new Vector2[]{new Vector2(0,0), new Vector2(1,0), new Vector2(1,1), new Vector2(0,1)}; + mesh.uv2 = mesh.uv; + + var terrGO = new GameObject(); + terrGO.name = "__ExportTerrain"; + GameObjectUtility.SetStaticEditorFlags(terrGO, StaticEditorFlags.LightmapStatic); + terrGO.transform.parent = terrParent.transform; + var mf = terrGO.AddComponent(); + var mr = terrGO.AddComponent(); + mf.sharedMesh = mesh; +#if UNITY_2019_3_OR_NEWER + // using standard materialTemplates in 2019.3 doesn't work + mr.sharedMaterial = unlitTerrainMat; +#else + mr.sharedMaterial = (terr.materialTemplate == null) ? unlitTerrainMat : terr.materialTemplate; +#endif + + terrGO.transform.position = obj.transform.position; + + var so2 = new SerializedObject(mr); + so2.FindProperty("m_ScaleInLightmap").floatValue = scaleInLmTerr; + so2.ApplyModifiedProperties(); + + //terrainObjectList.Add(terrGO); + //terrainObjectToActual.Add(terr); + } + else + { + for (int patchX=0; patchX(); + var mr = terrGO.AddComponent(); + mf.sharedMesh = mesh; +#if UNITY_2019_3_OR_NEWER + // using standard materialTemplates in 2019.3 doesn't work + mr.sharedMaterial = unlitTerrainMat; +#else + mr.sharedMaterial = (terr.materialTemplate == null) ? unlitTerrainMat : terr.materialTemplate; +#endif + + var so2 = new SerializedObject(mr); + so2.FindProperty("m_ScaleInLightmap").floatValue = scaleInLmTerr; + so2.ApplyModifiedProperties(); + + mr.shadowCastingMode = terr.castShadows ? UnityEngine.Rendering.ShadowCastingMode.On : UnityEngine.Rendering.ShadowCastingMode.Off; + + terrainObjectList.Add(terrGO); + terrainObjectToActual.Add(terr); + } + } + } + + if (exportTerrainTrees && terr.drawTreesAndFoliage) + { + var trees = tdata.treeInstances; + for (int t = 0; t < trees.Length; t++) + { + Vector3 pos = Vector3.Scale(trees[t].position, tdata.size) + obj.transform.position; + + var treeProt = tdata.treePrototypes[trees[t].prototypeIndex]; + var prefab = treeProt.prefab; + + var newObj = GameObject.Instantiate(prefab, pos, Quaternion.AngleAxis(trees[t].rotation, Vector3.up)) as GameObject; + newObj.name = "__Export" + newObj.name; + temporaryGameObjects.Add(newObj); + + var lodGroup = newObj.GetComponent(); + if (lodGroup == null) + { + var renderers = newObj.GetComponentsInChildren(); + for(int r=0; r(); + if (areaLightMesh != null) + { + var areaLight = obj.GetComponent(); + var mr = GetValidRenderer(obj); + var mf = obj.GetComponent(); + + if (!forceAllAreaLightsSelfshadow) + { + if (!areaLightMesh.selfShadow) return true; // no selfshadow - ignore mesh export + } + if (areaLight != null && ftLightMeshInspector.IsArea(areaLight) && (mr == null || mf == null)) + { + var areaObj = new GameObject(); + mf = areaObj.AddComponent(); + mf.sharedMesh = BuildAreaLightMesh(areaLight); + mr = areaObj.AddComponent(); + + var props = new MaterialPropertyBlock(); + props.SetColor("_Color", areaLightMesh.color); + props.SetFloat("intensity", areaLightMesh.intensity); + if (areaLightMesh.texture != null) props.SetTexture("_MainTex", areaLightMesh.texture); + mr.SetPropertyBlock(props); + GameObjectUtility.SetStaticEditorFlags(areaObj, StaticEditorFlags.LightmapStatic); + temporaryAreaLightMeshList.Add(areaObj); + temporaryAreaLightMeshList2.Add(areaLightMesh); + + var xformSrc = obj.transform; + var xformDest = areaObj.transform; + xformDest.position = xformSrc.position; + xformDest.rotation = xformSrc.rotation; + var srcMtx = xformSrc.localToWorldMatrix; + xformDest.localScale = new Vector3(srcMtx.GetColumn(0).magnitude, srcMtx.GetColumn(1).magnitude, srcMtx.GetColumn(2).magnitude); + + return true; // mesh created + } + } + return false; // not Light Mesh + } + + static void MapObjectsToSceneLODs(ExportSceneData data, UnityEngine.Object[] objects) + { + var objToLodLevel = data.objToLodLevel; + var objToLodLevelVisible = data.objToLodLevelVisible; + lodLevelsVisibleInLodLevel = new Dictionary>(); + + const int maxSceneLodLevels = 100; + var sceneLodUsed = new int[maxSceneLodLevels]; + for(int i=0; i[lodGroups.Length]; + var localLodLevelsInLodGroup = new List[lodGroups.Length]; + int lcounter = -1; + foreach(LODGroup lodgroup in lodGroups) + { + lcounter++; + if (!lodgroup.enabled) continue; + var obj = lodgroup.gameObject; + if (obj == null) continue; + if (!obj.activeInHierarchy) continue; + var path = AssetDatabase.GetAssetPath(obj); + if (path != "") continue; // must belond to scene + if ((obj.hideFlags & (HideFlags.DontSave|HideFlags.HideAndDontSave)) != 0) continue; // skip temp objects + if (obj.tag == "EditorOnly") continue; // skip temp objects + + var lods = lodgroup.GetLODs(); + if (lods.Length == 0) continue; + + for(int i=0; i() != null; + if (!mrEnabled) continue; + //if (mf.sharedMesh == null) continue; + + var so = new SerializedObject(mr); + var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue; // (before FilterObjects) + if (scaleInLm == 0) continue; + + lightmappedLOD = true; + break; + } + if (!lightmappedLOD) continue; + + var lodDist = i == 0 ? 0 : (int)Mathf.Clamp((1.0f-lods[i-1].screenRelativeTransitionHeight) * (maxSceneLodLevels-1), 0, maxSceneLodLevels-1); + if (sceneLodUsed[lodDist] < 0) + { + sceneLodUsed[lodDist] = sceneLodsUsed; + sceneLodsUsed++; + } + int newLodLevel = sceneLodUsed[lodDist]; + + if (lodLevelsInLodGroup[lcounter] == null) + { + lodLevelsInLodGroup[lcounter] = new List(); + localLodLevelsInLodGroup[lcounter] = new List(); + } + if (lodLevelsInLodGroup[lcounter].IndexOf(newLodLevel) < 0) + { + lodLevelsInLodGroup[lcounter].Add(newLodLevel); + localLodLevelsInLodGroup[lcounter].Add(i); + } + + for(int j=0; j visList; + if (!objToLodLevelVisible.TryGetValue(r.gameObject, out visList)) objToLodLevelVisible[r.gameObject] = visList = new List(); + visList.Add(newLodLevel); + } + } + } + } + + // Sort scene LOD levels + int counter = 0; + var unsortedLodToSortedLod = new int[maxSceneLodLevels]; + for(int i=0; i= 0) + { + unsortedLodToSortedLod[unsorted] = counter; + sceneLodUsed[i] = counter; + counter++; + } + } + var keys = new GameObject[objToLodLevel.Count]; + counter = 0; + foreach(var pair in objToLodLevel) + { + keys[counter] = pair.Key; + counter++; + } + foreach(var key in keys) + { + int unsorted = objToLodLevel[key]; + objToLodLevel[key] = unsortedLodToSortedLod[unsorted]; + var visList = objToLodLevelVisible[key]; + for(int j=0; j 1) + { + var lodRenderers = lods[localLevel].renderers; + for(int k=0; k visList; + if (!lodLevelsVisibleInLodLevel.TryGetValue(affectedLOD, out visList)) lodLevelsVisibleInLodLevel[affectedLOD] = visList = new List(); + if (visList.IndexOf(objOwnLOD) < 0) visList.Add(objOwnLOD); + } + } + /*foreach(var pair in lodLevelsVisibleInLodLevel) + { + string str = "LOD " + pair.Key + " sees: "; + for(int i=0; i[sceneLodsUsed]; + data.indicesTransparentLOD = new List[sceneLodsUsed]; + for(int i=0; i(); + data.indicesTransparentLOD[i] = new List(); + } + + // Sort objects by scene-wide LOD level + if (sceneLodsUsed > 0) + { + Array.Sort(objects, delegate(UnityEngine.Object a, UnityEngine.Object b) + { + if (a == null || b == null) return 0; + int lodLevelA = -1; + int lodLevelB = -1; + if (!objToLodLevel.TryGetValue((GameObject)a, out lodLevelA)) lodLevelA = -1; + if (!objToLodLevel.TryGetValue((GameObject)b, out lodLevelB)) lodLevelB = -1; + return lodLevelA.CompareTo(lodLevelB); + }); + } + } + + public static int IsInsideSector(Transform tform, Transform sectorTform, Bounds b, BakerySector s) + { + var parent = tform; + while(parent != null) + { + if (parent == sectorTform) return 1; // belongs to this sector + parent = parent.parent; + } + + return 0; // far + } + + static bool ClipFarSphere(FarSphereRenderData clipee, FarSphereRenderData clipper) + { + if (farSphereProjClipShader == null) + { + farSphereProjClipShader = Shader.Find("Hidden/ftFarSphereProjClip"); + if (farSphereProjClipShader == null) + { + Debug.LogError("Can't find ftFarSphereProjClip shader"); + return false; + } + } + + if (farSphereProjClipMat == null) + { + farSphereProjClipMat = new Material(farSphereProjClipShader); + } + + // Project clipper depth to clipee + // If projected pixel is on screen and is closer to the projector + // draw empty pixel + // else + // discard + var cmd = new CommandBuffer(); + Shader.SetGlobalFloat("_InvCubeSize", 1.0f / ftAdditionalConfig.sectorFarSphereResolution); + for(int i=0; i<6; i++) + { + Shader.SetGlobalTexture("_CurDepth", clipee.depth[i]); + Shader.SetGlobalMatrix("_CurInvViewProj", Matrix4x4.Inverse(clipee.viewProj[i])); + Shader.SetGlobalVector("_CurPos", clipee.pos); + Shader.SetGlobalTexture("_CurNormal", clipee.normal[i]); + + for(int j=0; j<6; j++) + { + Shader.SetGlobalTexture("_ProjDepth", clipper.depth[j]); + Shader.SetGlobalMatrix("_ProjViewProj", clipper.viewProj[j]); + Shader.SetGlobalVector("_ProjPos", clipper.pos); + + //Graphics.Blit(null, clipee.albedo[i], farSphereProjClipMat); // can flip + + cmd.Clear(); + cmd.SetRenderTarget(new RenderTargetIdentifier(clipee.albedo[i]), 0, CubemapFace.Unknown, 0); + cmd.DrawProcedural(Matrix4x4.identity, farSphereProjClipMat, 0, MeshTopology.Triangles, 6, 1, null); + Graphics.ExecuteCommandBuffer(cmd); + } + } + + return true; + } + + static FarSphereRenderData GenerateFarSphereData(ExportSceneData data, Vector3 capturePoint, int objCount, bool genProjMatrix) + { + // Constants + const int cubeRes = ftAdditionalConfig.sectorFarSphereResolution; + + var rot = new Vector3[6]; + rot[0] = new Vector3(0, -90, 0); + rot[1] = new Vector3(0, 90, 0); + rot[2] = new Vector3(90, 0, 0); + rot[3] = new Vector3(-90, 0, 0); + rot[4] = new Vector3(0, 180, 180); + rot[5] = new Vector3(0, 0, 180); + + var wnormal = new Vector3[6]; + wnormal[0] = -Vector3.right; + wnormal[1] = Vector3.right; + wnormal[2] = -Vector3.up; + wnormal[3] = Vector3.up; + wnormal[4] = -Vector3.forward; + wnormal[5] = Vector3.forward; + + var objsToWrite = data.objsToWrite; + //var sector = ftRenderLightmap.curSector; + + // Load shaders + if (farSphereRshader == null) + { + farSphereRshader = Shader.Find("Hidden/ftFarSphereRender"); + if (farSphereRshader == null) + { + Debug.LogError("Can't find ftFarSphereRender shader"); + return null; + } + } + + if (farSphereRshaderOcc == null) + { + farSphereRshaderOcc = Shader.Find("Hidden/ftFarSphereRenderOccluder"); + if (farSphereRshaderOcc == null) + { + Debug.LogError("Can't find ftFarSphereRenderOccluder shader"); + return null; + } + } + + if (farSphereMatOcc == null) + { + farSphereMatOcc = new Material(farSphereRshaderOcc); + } + + // Create RTs + FarSphereRenderData outData = new FarSphereRenderData(); + outData.albedo = new RenderTexture[6]; + outData.depth = new RenderTexture[6]; + outData.normal = new RenderTexture[6]; + outData.viewProj = new Matrix4x4[6]; + outData.pos = capturePoint; + for(int i=0; i<6; i++) + { + outData.albedo[i] = new RenderTexture(cubeRes, cubeRes, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); + outData.albedo[i].wrapMode = TextureWrapMode.Clamp; + outData.albedo[i].Create(); + outData.depth[i] = new RenderTexture(cubeRes, cubeRes, 32, RenderTextureFormat.Depth, RenderTextureReadWrite.Linear); + outData.depth[i].filterMode = FilterMode.Point; + outData.depth[i].wrapMode = TextureWrapMode.Clamp; + outData.depth[i].Create(); + outData.normal[i] = new RenderTexture(cubeRes, cubeRes, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); + outData.normal[i].filterMode = FilterMode.Point; + outData.normal[i].wrapMode = TextureWrapMode.Clamp; + outData.normal[i].Create(); + } + + bool useCamera = true; + List outsideRenderers = null; +//#if UNITY_2019_1_OR_NEWER +// if (GraphicsSettings.renderPipelineAsset != null) +// { + useCamera = false; + outsideRenderers = data.outsideRenderers; + if (farSphereMat == null) + { + farSphereMat = new Material(farSphereRshader); + } +// } +//#endif + + GameObject tempCamGO = null; + Camera tempCam = null; + Transform camTform = null; + Matrix4x4 uProj; + if (useCamera) + { + // Create temp camera + tempCamGO = new GameObject(); + tempCamGO.name = "BakerySectorFarCamera"; + camTform = tempCamGO.transform; + camTform.localPosition = capturePoint;// sectorCenter; + tempCam = tempCamGO.AddComponent(); + tempCam.orthographic = false; + tempCam.aspect = 1.0f; + tempCam.fieldOfView = 90.0f; + tempCam.enabled = false; + tempCam.clearFlags = CameraClearFlags.Nothing; + tempCam.backgroundColor = new Color(0,0,0,0); + tempCam.SetReplacementShader(farSphereRshader, "RenderType"); + tempCam.nearClipPlane = 0.1f; + tempCam.farClipPlane = 1000.0f; + tempCam.renderingPath = RenderingPath.Forward; + + uProj = tempCam.projectionMatrix; + } + else + { + uProj = Matrix4x4.Perspective(90, 1, 0.1f, 1000.0f); + } + + var proj = GL.GetGPUProjectionMatrix(uProj, true); + + /*// Set culling matrices for the render (cull things inside the sector + near distance) + if (sector.tforms.Count == 0) + { + Shader.SetGlobalFloat("cullMatricesCount", 0); + } + else + { + var cullMatrices = new Matrix4x4[sector.tforms.Count]; + for(int i=0; i().sharedMesh; + var worldMatrix = objsToWrite[o].transform.localToWorldMatrix; + for(int s=0; s().sharedMesh; + var worldMatrix = outsideRenderers[o].transform.localToWorldMatrix; + var mats = outsideRenderers[o].sharedMaterials; + for(int s=0; s s && mats[s] != null && mats[s].HasProperty("_MainTex")) + { + var mat = mats[s]; + Texture tex = null; + + if (mat.HasProperty("_MainTex")) + { + tex = mat.mainTexture; + } + else if (mat.HasProperty("_BaseColorMap")) + { + // HDRP + tex = mat.GetTexture("_BaseColorMap"); + } + else if (mat.HasProperty("_BaseMap")) + { + // URP + tex = mat.GetTexture("_BaseMap"); + } + + farSphereMat.SetTexture("_MainTex", tex); + + pass = 0; // opaque + if (tex != null) + { + var matTag = mat.GetTag("RenderType", true); + if (matTag == "TransparentCutout" || matTag == "Transparent" || matTag == "TreeLeaf") + { + pass = 2; // transparent cutout + } + } + + farSphereMat.SetPass(pass); + } + + for(int j=0; j capturePoints) + { + if (fdatas.Length == 0) return true; + + // Constants + const int cubeRes = ftAdditionalConfig.sectorFarSphereResolution; + const int threadWidth = 16; + const int vertWidth = cubeRes; + const int dispatchWidth = vertWidth / threadWidth; + const int triCount = (vertWidth-1)*(vertWidth-1)*2; + int dispatchIndexGroups = (int)Mathf.Ceil(triCount / (float)(threadWidth * threadWidth)); + + var wnormal = new Vector3[6]; + wnormal[0] = -Vector3.right; + wnormal[1] = Vector3.right; + wnormal[2] = -Vector3.up; + wnormal[3] = Vector3.up; + wnormal[4] = -Vector3.forward; + wnormal[5] = Vector3.forward; + + var objsToWrite = data.objsToWrite; + var objsToWriteNames = data.objsToWriteNames; + var objsToWriteLightmapped = data.objsToWriteLightmapped; + var objsToWriteGroup = data.objsToWriteGroup; + var objsToWriteHolder = data.objsToWriteHolder; + var objsToWriteVerticesUV = data.objsToWriteVerticesUV; + var objsToWriteVerticesUV2 = data.objsToWriteVerticesUV2; + var objsToWriteIndices = data.objsToWriteIndices; + var objToScaleInLm = data.objToScaleInLm; + if (data.autoVertexGroup == null) CreateAutoVertexGroup(data, data.objsToWrite.Count > 0 ? data.objsToWrite[0] : null); + var storages = data.storages; + var sceneToID = data.sceneToID; + + if (farSphereSshader == null) + { + farSphereSshader = Shader.Find("Hidden/ftFarSphere"); + if (farSphereSshader == null) + { + Debug.LogError("Can't find ftFarSphere shader"); + return false; + } + } + + var bakeryEditorPath = ftLightmaps.GetEditorPath(); + + if (farSphereCSTransform == null) + { + farSphereCSTransform = AssetDatabase.LoadAssetAtPath(bakeryEditorPath + "shaderSrc/ftTransformFarSphere.compute", typeof(ComputeShader)) as ComputeShader; + if (farSphereCSTransform == null) + { + Debug.LogError("Can't find ftTransformFarSphere"); + return false; + } + } + + if (farSphereCSCull == null) + { + farSphereCSCull = AssetDatabase.LoadAssetAtPath(bakeryEditorPath + "shaderSrc/ftCullFarSphere.compute", typeof(ComputeShader)) as ComputeShader; + if (farSphereCSCull == null) + { + Debug.LogError("Can't find ftCullFarSphere"); + return false; + } + } + + // Create temp buffers + var rwBuff = new ComputeBuffer(vertWidth * vertWidth, 12); // vertices in/out + var indBuff = new ComputeBuffer(triCount, 3*4); // indices in + var appendBuff = new ComputeBuffer(triCount, 3*4, ComputeBufferType.Append); // indices out + var countBuff = new ComputeBuffer(1, 4, ComputeBufferType.Raw); // out index count + var countArray = new int[1]; + var uvBuff = new ComputeBuffer(vertWidth * vertWidth, 8); // vertex UVs + + // Create shared input index buffer + var tris = new int[(vertWidth-1) * (vertWidth-1) * 2 * 3]; + int indCount = 0; + for(int y=0; y(); + var so = new SerializedObject(mr); + var scaleInLm = so.FindProperty("m_ScaleInLightmap"); + scaleInLm.floatValue = 0; + so.ApplyModifiedProperties(); + mr.sharedMaterial = mat; + GameObjectUtility.SetStaticEditorFlags(faceMesh, StaticEditorFlags.LightmapStatic); + + var mf = faceMesh.AddComponent(); + + var mesh = new Mesh(); + mesh.vertices = verts; + mesh.uv = uv; + mesh.triangles = arr; + totalTris += arr.Length; + mesh.RecalculateNormals(); + mf.sharedMesh = mesh; + fdata.meshes[i] = mesh; + + objsToWrite.Add(faceMesh); + objsToWriteNames.Add(""); + objsToWriteLightmapped.Add(false); + objsToWriteGroup.Add(data.autoVertexGroup); + objsToWriteHolder.Add(faceMesh); + objsToWriteVerticesUV.Add(uv); + objsToWriteVerticesUV2.Add(uv); + var inds = new int[1][]; + inds[0] = arr; + objsToWriteIndices.Add(inds); + objToScaleInLm[faceMesh] = 0; + + if (sceneST == null) sceneST = storages[sceneToID[faceMesh.scene]]; + sceneST.implicitGroupedObjects.Add(faceMesh); + sceneST.implicitGroups.Add(data.autoVertexGroup); + tempStorage.implicitGroupMap[faceMesh] = data.autoVertexGroup; + + if (modifyLightmapStorage) sceneST.nonBakedRenderers.Add(mr); + + fdata.albedo[i].Release(); + fdata.depth[i].Release(); + fdata.normal[i].Release(); + } + } + + Debug.Log("Total out-of-sector tris: " + totalTris); + + tempTex.Release(); + rwBuff.Release(); + indBuff.Release(); + appendBuff.Release(); + countBuff.Release(); + uvBuff.Release(); + + return true; + } + + static bool LoadSectorCapture(ExportSceneData data, BakerySectorCapture capture, Transform sectorTform) + { + if (capture.meshes == null) + { + Debug.LogError("No meshes in capture"); + return false; + } + if (capture.positions == null) + { + Debug.LogError("No positions in capture"); + return false; + } + if (capture.textures == null) + { + Debug.LogError("No textures in capture"); + return false; + } + + if (farSphereSshader == null) + { + farSphereSshader = Shader.Find("Hidden/ftFarSphere"); + if (farSphereSshader == null) + { + Debug.LogError("Can't find ftFarSphere shader"); + return false; + } + } + + var objsToWrite = data.objsToWrite; + var objsToWriteNames = data.objsToWriteNames; + var objsToWriteLightmapped = data.objsToWriteLightmapped; + var objsToWriteGroup = data.objsToWriteGroup; + var objsToWriteHolder = data.objsToWriteHolder; + var objsToWriteVerticesUV = data.objsToWriteVerticesUV; + var objsToWriteVerticesUV2 = data.objsToWriteVerticesUV2; + var objsToWriteIndices = data.objsToWriteIndices; + var objToScaleInLm = data.objToScaleInLm; + if (data.autoVertexGroup == null) CreateAutoVertexGroup(data, data.objsToWrite.Count > 0 ? data.objsToWrite[0] : null); + var storages = data.storages; + var sceneToID = data.sceneToID; + + var parent = new GameObject(); + parent.name = "fp"; + var parentTform = parent.transform; + parentTform.position = capture.sectorPos; + parentTform.rotation = capture.sectorRot; + temporaryGameObjects.Add(parent); + + ftLightmapsStorage sceneST = null; + + for(int i=0; i(); + var so = new SerializedObject(mr); + var scaleInLm = so.FindProperty("m_ScaleInLightmap"); + scaleInLm.floatValue = 0; + so.ApplyModifiedProperties(); + mr.sharedMaterial = mat; + GameObjectUtility.SetStaticEditorFlags(faceMesh, StaticEditorFlags.LightmapStatic); + + var mf = faceMesh.AddComponent(); + mf.sharedMesh = capture.meshes[i]; + + objsToWrite.Add(faceMesh); + objsToWriteNames.Add(""); + objsToWriteLightmapped.Add(false); + objsToWriteGroup.Add(data.autoVertexGroup); + objsToWriteHolder.Add(faceMesh); + + var uv = mf.sharedMesh.uv; + objsToWriteVerticesUV.Add(uv); + objsToWriteVerticesUV2.Add(uv); + + var inds = new int[1][]; + inds[0] = mf.sharedMesh.triangles; + objsToWriteIndices.Add(inds); + objToScaleInLm[faceMesh] = 0; + + if (sceneST == null) sceneST = storages[sceneToID[faceMesh.scene]]; + sceneST.implicitGroupedObjects.Add(faceMesh); + sceneST.implicitGroups.Add(data.autoVertexGroup); + tempStorage.implicitGroupMap[faceMesh] = data.autoVertexGroup; + + if (modifyLightmapStorage) sceneST.nonBakedRenderers.Add(mr); + } + + parentTform.rotation = sectorTform.rotation; + parentTform.position = sectorTform.position; + + return true; + } + + static void CreateAutoVertexGroup(ExportSceneData data, GameObject anyObj) + { + var groupList = data.groupList; + var lmBounds = data.lmBounds; + + data.autoVertexGroup = ScriptableObject.CreateInstance(); + data.autoVertexGroup.name = (anyObj == null ? "scene" : anyObj.scene.name) + "_VLM"; + data.autoVertexGroup.isImplicit = true; + data.autoVertexGroup.resolution = 256; + data.autoVertexGroup.bitmask = 1; + data.autoVertexGroup.mode = BakeryLightmapGroup.ftLMGroupMode.Vertex; + data.autoVertexGroup.id = data.lmid; + groupList.Add(data.autoVertexGroup); + lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0))); + data.lmid++; + } + + static bool FilterObjects(ExportSceneData data, UnityEngine.Object[] objects) + { + var objToLodLevel = data.objToLodLevel; + var storages = data.storages; + var sceneToID = data.sceneToID; + var objsToWrite = data.objsToWrite; + var objsToWriteNames = data.objsToWriteNames; + var objsToWriteLightmapped = data.objsToWriteLightmapped; + var objsToWriteGroup = data.objsToWriteGroup; + var objsToWriteHolder = data.objsToWriteHolder; + var objsToWriteVerticesUV = data.objsToWriteVerticesUV; + var objsToWriteVerticesUV2 = data.objsToWriteVerticesUV2; + var objsToWriteIndices = data.objsToWriteIndices; + var objToScaleInLm = data.objToScaleInLm; + List outsideRenderers = null; + + outsideRenderers = data.outsideRenderers; +/*#if UNITY_2019_1_OR_NEWER + if (GraphicsSettings.renderPipelineAsset != null) + { + outsideRenderers = data.outsideRenderers; + } +#endif*/ + Transform sectorTform = null; + if (ftRenderLightmap.fullSectorRender) + { + sectorTform = ftRenderLightmap.curSector.transform; + } + + var prop = new MaterialPropertyBlock(); + foreach(GameObject obj in objects) + { + if (obj == null) continue; + if (!obj.activeInHierarchy) continue; + var path = AssetDatabase.GetAssetPath(obj); + if (path != "") continue; // must belond to scene + if ((obj.hideFlags & (HideFlags.DontSave|HideFlags.HideAndDontSave)) != 0) continue; // skip temp objects + if (obj.tag == "EditorOnly") continue; // skip temp objects + + var areaLight = obj.GetComponent(); + if (areaLight == null) + { + int areaIndex = temporaryAreaLightMeshList.IndexOf(obj); + if (areaIndex >= 0) areaLight = temporaryAreaLightMeshList2[areaIndex]; + } + + if (areaLight != null) + { + if (!forceAllAreaLightsSelfshadow) + { + if (!areaLight.selfShadow) continue; + } + } + var mr = GetValidRenderer(obj); + + if (mr == null) + { + // must be MR or SMR + continue; + } + + var sharedMesh = GetSharedMesh(mr); + if (sharedMesh == null) continue; // must have visible mesh + + // Remove previous lightmap +#if UNITY_2018_1_OR_NEWER + if (mr.HasPropertyBlock()) + { + // Reset shader props + mr.GetPropertyBlock(prop); + prop.SetFloat("bakeryLightmapMode", 0); + mr.SetPropertyBlock(prop); + } +#else + mr.GetPropertyBlock(prop); + if (!prop.isEmpty) + { + prop.SetFloat("bakeryLightmapMode", 0); + mr.SetPropertyBlock(prop); + } +#endif + if (((GameObjectUtility.GetStaticEditorFlags(obj) & StaticEditorFlags.LightmapStatic) == 0) && areaLight==null) + { + mr.lightmapIndex = 0xFFFF; + continue; // skip dynamic + } + + var mrEnabled = mr.enabled || obj.GetComponent() != null; + if (!mrEnabled && areaLight == null) continue; + + var so = new SerializedObject(mr);//obj.GetComponent()); + var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue; + + if (ftRenderLightmap.fullSectorRender) + { + int status = IsInsideSector(obj.transform, sectorTform, mr.bounds, ftRenderLightmap.curSector); + if (status == 0) + { + if (outsideRenderers != null) outsideRenderers.Add(mr); + continue; + } + if (status == 2) scaleInLm = 0; + } + + objToScaleInLm[obj] = scaleInLm; + + BakeryLightmapGroup group = null; + if (scaleInLm > 0) + { + group = GetLMGroupFromObjectExplicit(obj, data); + if (group != null) + { + // Set LOD level for explicit group + int lodLevel; + if (!objToLodLevel.TryGetValue(obj, out lodLevel)) lodLevel = -1; + + var packer = group.atlasPacker == BakeryLightmapGroup.AtlasPacker.Auto ? atlasPacker : (ftGlobalStorage.AtlasPacker)group.atlasPacker; + + if (!postPacking || packer != ftGlobalStorage.AtlasPacker.xatlas) + { + if (group.sceneLodLevel == -1) + { + group.sceneLodLevel = lodLevel; + } + else + { + if (!ExportSceneValidationMessage("Multiple LOD levels in " + group.name + ", this is only supported when xatlas is set as the atlas packer and post-packing is enabled.")) return false; + } + } + + if (splitByScene) group.sceneName = obj.scene.name; + + // New explicit Pack Atlas holder selection + if (!group.isImplicit && group.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas) + { + lmgroupHolder = obj; // by default pack each object + lmgroupHolder = TestPackAsSingleSquare(lmgroupHolder); + var prefabParent = PrefabUtility.GetPrefabParent(obj) as GameObject; + if (prefabParent != null) + { + var ptype = PrefabUtility.GetPrefabType(prefabParent); + if (ptype == PrefabType.ModelPrefab) + { + // but if object is a part of prefab/model + var sharedMesh2 = GetSharedMesh(obj); + var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(sharedMesh2)) as ModelImporter; + if (importer != null && !ModelUVsOverlap(importer, gstorage)) + { + // or actually just non-overlapping model, + // then pack it as a whole + + // find topmost asset parent + var t = prefabParent.transform; + while(t.parent != null) t = t.parent; + var assetTopMost = t.gameObject; + + // find topmost scene instance parent + var g = obj; + while(PrefabUtility.GetPrefabParent(g) as GameObject != assetTopMost && g.transform.parent != null) + { + g = g.transform.parent.gameObject; + } + lmgroupHolder = g; + } + } + } + } + } + } + else + { + if (data.autoVertexGroup == null) CreateAutoVertexGroup(data, obj); + group = data.autoVertexGroup; + + tempStorage.implicitGroupMap[obj] = data.autoVertexGroup; + + if (modifyLightmapStorage) + { + var st = storages[sceneToID[obj.scene]]; + st.implicitGroupedObjects.Add(obj); + st.implicitGroups.Add(data.autoVertexGroup); + st.nonBakedRenderers.Add(mr); + } + } + + bool vertexBake = (group != null && group.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex); + // must have UVs or be arealight or vertexbaked + var uv = sharedMesh.uv; + var uv2 = sharedMesh.uv2; + if (uv.Length == 0 && uv2.Length == 0 && areaLight==null && !vertexBake) continue; + + var usedUVs = uv2.Length == 0 ? uv : uv2; + //bool validUVs = true; + for(int v=0; v 1.0001f || usedUVs[v].y < -0.0001f || usedUVs[v].y > 1.0001f) + { + DebugLogWarning("Mesh " + sharedMesh.name + " on object " + obj.name + " possibly has incorrect UVs (UV2: " + (uv2.Length == 0 ? "no" : "yes")+", U: " + usedUVs[v].x + ", V: " + usedUVs[v].y + ")"); + //validUVs = false; + break; + } + } + //if (!validUVs) continue; + + if (vertexBake) + { + group.totalVertexCount = 0; + group.vertexCounter = 0; + } + + objsToWrite.Add(obj); + objsToWriteNames.Add(obj.name); + objsToWriteLightmapped.Add((scaleInLm > 0 && areaLight == null) ? true : false); + objsToWriteGroup.Add(group); + objsToWriteHolder.Add(lmgroupHolder); + + objsToWriteVerticesUV.Add(uv); + objsToWriteVerticesUV2.Add(uv2); + var inds = new int[sharedMesh.subMeshCount][]; + for(int n=0; n 0) + { + newGroup = autoAtlasGroups[0]; + } + else + { + newGroup = ScriptableObject.CreateInstance(); + + // Make sure first lightmap is always LM0, not LM1, if probes are used + int lmNum = storages[sceneToID[holder.scene]].implicitGroups.Count; + if (ftRenderLightmap.lightProbeMode == ftRenderLightmap.LightProbeMode.L1 && ftRenderLightmap.hasAnyProbes && renderTextures && !atlasOnly) lmNum--; + + newGroup.name = holder.scene.name + "_LM" + autoAtlasGroups.Count;//lmNum; + newGroup.isImplicit = true; + newGroup.resolution = 256; + newGroup.bitmask = 1; + newGroup.area = 0; + newGroup.mode = autoAtlas ? BakeryLightmapGroup.ftLMGroupMode.PackAtlas : BakeryLightmapGroup.ftLMGroupMode.OriginalUV; + + newGroup.id = data.lmid; + groupList.Add(newGroup); + lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0))); + data.lmid++; + + if (autoAtlas) + { + autoAtlasGroups.Add(newGroup); + var rootNode = new AtlasNode(); + rootNode.rc = new Rect(0, 0, 1, 1); + autoAtlasGroupRootNodes.Add(rootNode); + } + } + storages[sceneToID[holder.scene]].implicitGroupedObjects.Add(holder); + + storages[sceneToID[holder.scene]].implicitGroups.Add(newGroup); + //Debug.LogError("Add "+(storages[sceneToID[holder.scene]].implicitGroups.Count-1)+" "+newGroup.name); + + tempStorage.implicitGroupMap[holder] = newGroup; + if (splitByScene) newGroup.sceneName = holder.scene.name; + } + + if (!tempStorage.implicitGroupMap.ContainsKey(holder)) + { + // happens with modifyLightmapStorage == false + var gholders = storages[sceneToID[holder.scene]].implicitGroupedObjects; + var grs = storages[sceneToID[holder.scene]].implicitGroups; + for(int g=0; g= 0) + { + startIndex = onlyID; + endIndex = onlyID; + } + + // Transform vertices to world space + for(int i=startIndex; i<=endIndex; i++) + { + var obj = objsToWrite[i]; + var lmgroup = objsToWriteGroup[i]; + bool isSkin; + var m = GetSharedMeshSkinned(obj, out isSkin); + var vertices = m.vertices; + var tform = obj.transform; + + while(objsToWriteVerticesPosW.Count <= i) + { + objsToWriteVerticesPosW.Add(null); + objsToWriteVerticesNormalW.Add(null); + } + + objsToWriteVerticesPosW[i] = new Vector3[vertices.Length]; + if (isSkin) + { + var lossyScale = tform.lossyScale; + var inverseWorldScale = new Vector3(1.0f/lossyScale.x, 1.0f/lossyScale.y, 1.0f/lossyScale.z); + for(int t=0; t(); + var objsWithExplicitGroupPadding = new List(); + var objsWithExplicitGroupPaddingWidth = new List(); + + for(int i=0; i()); + //var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue; + var scaleInLm = data.objToScaleInLm[objsToWrite[i]]; + if (scaleInLm == 0) continue; // don't reunwrap objects with scale in lightmap == 0 + + area *= scaleInLm; + + float width = Mathf.Sqrt(area); + float twidth = 1; + if (lmgroup.isImplicit) + { + twidth = width * texelsPerUnit; + } + else + { + float currentArea; + if (!explicitGroupTotalArea.TryGetValue(lmgroup.id, out currentArea)) currentArea = 0; + explicitGroupTotalArea[lmgroup.id] = currentArea + area; + + var holder = objsToWriteHolder[i]; + BakeryLightmapGroupSelector comp = null; + if (holder != null) comp = holder.GetComponent(); + if (comp != null && comp.instanceResolutionOverride) + { + // Explicit holder size + twidth = width * comp.instanceResolution; + } + else + { + // Texel size in atlas - can't calculate at this point + objsWithExplicitGroupPadding.Add(i); + objsWithExplicitGroupPaddingWidth.Add(width); + continue; + } + } + float requiredPadding = 4 * (1024.0f / (twidth * smallestMapScale)); + int requiredPaddingClamped = (int)Mathf.Clamp(requiredPadding, 1, 256); + + int existingPadding = 0; + meshToPaddingMap.TryGetValue(m, out existingPadding); + meshToPaddingMap[m] = Math.Max(requiredPaddingClamped, existingPadding); // select largest padding among instances + + List arr; + if (!meshToObjIDs.TryGetValue(m, out arr)) + { + meshToObjIDs[m] = arr = new List(); + } + if (!arr.Contains(i)) arr.Add(i); + } + + for(int j=0; j arr; + if (!meshToObjIDs.TryGetValue(m, out arr)) + { + meshToObjIDs[m] = arr = new List(); + } + if (!arr.Contains(i)) arr.Add(i); + } + } + + static void ResetPaddingStorageData(ExportSceneData data) + { + var storages = data.storages; + + // Reset scene padding backup + for(int s=0; s(); + str.modifiedAssets = new List(); + } + } + + static void StoreNewUVPadding(ExportSceneData data, AdjustUVPaddingData adata) + { + var meshToPaddingMap = adata.meshToPaddingMap; + var meshToObjIDs = adata.meshToObjIDs; + var dirtyAssetList = adata.dirtyAssetList; + var dirtyObjList = adata.dirtyObjList; + var storages = data.storages; + + foreach(var pair in meshToPaddingMap) + { + var m = pair.Key; + var requiredPaddingClamped = pair.Value; + var assetPath = AssetDatabase.GetAssetPath(m); + + var ids = meshToObjIDs[m]; + + //for(int s=0; s= 0) ind = objStorage.modifiedAssets[mstoreIndex].meshName.IndexOf(mname); + if (ind < 0) + { + if (mstoreIndex < 0) + { + // add new record to globalstorage + objStorage.modifiedAssetPathList.Add(assetPath); + var newStruct = new ftGlobalStorage.AdjustedMesh(); + newStruct.meshName = new List(); + newStruct.padding = new List(); + objStorage.modifiedAssets.Add(newStruct); + mstoreIndex = objStorage.modifiedAssets.Count - 1; + } + + var nameList = objStorage.modifiedAssets[mstoreIndex].meshName; + var paddingList = objStorage.modifiedAssets[mstoreIndex].padding; + var unwrapperList = objStorage.modifiedAssets[mstoreIndex].unwrapper; + if (unwrapperList == null) + { + var s = objStorage.modifiedAssets[mstoreIndex]; + unwrapperList = s.unwrapper = new List(); + objStorage.modifiedAssets[mstoreIndex] = s; + } + while(nameList.Count > unwrapperList.Count) unwrapperList.Add(0); // fix legacy + + nameList.Add(mname); + paddingList.Add(requiredPaddingClamped); + unwrapperList.Add((int)ftRenderLightmap.unwrapper); + + if (!dirtyAssetList.Contains(assetPath)) dirtyAssetList.Add(assetPath); + for(int xx=0; xx(); + objStorage.modifiedAssets[mstoreIndex] = s; + } + while(nameList.Count > unwrapperList.Count) unwrapperList.Add(0); // fix legacy + + // modify existing record + var oldValue = paddingList[ind]; + var oldUnwrapperValue = (ftGlobalStorage.Unwrapper)unwrapperList[ind]; + bool shouldModify = oldValue != requiredPaddingClamped; + if (uvPaddingMax) + { + shouldModify = oldValue < requiredPaddingClamped; + } + if (oldUnwrapperValue != ftRenderLightmap.unwrapper) shouldModify = true; + if (shouldModify) + { + if (!dirtyAssetList.Contains(assetPath)) dirtyAssetList.Add(assetPath); + for(int xx=0; xx 0) + { + sceneNeedsToBeRebuilt = true; + return false; + } + } + return true; + } + + static bool ValidateScaleOffsetImmutability(ExportSceneData data) + { + if (validateLightmapStorageImmutability) + { + var holderRect = data.holderRect; + var objsToWrite = data.objsToWrite; + var objsToWriteGroup = data.objsToWriteGroup; + var objsToWriteHolder = data.objsToWriteHolder; + var storages = data.storages; + var sceneToID = data.sceneToID; + + var emptyVec4 = new Vector4(1,1,0,0); + Rect rc = new Rect(); + for(int i=0; i(); + + var vpos = objsToWriteVerticesPosW[i]; + var vuv = objsToWriteVerticesUV2[i];//m.uv2; + var inds = objsToWriteIndices[i]; + //if (vuv.Length == 0 || obj.GetComponent()!=null) vuv = objsToWriteVerticesUV[i];//m.uv; // area lights or objects without UV2 export UV1 instead + if (vuv.Length == 0 || obj.GetComponent()!=null || temporaryAreaLightMeshList.Contains(obj)) vuv = objsToWriteVerticesUV[i];//m.uv; // area lights or objects without UV2 export UV1 instead + Vector2 uv1 = Vector2.zero; + Vector2 uv2 = Vector2.zero; + Vector2 uv3 = Vector2.zero; + + int lodLevel; + if (!objToLodLevel.TryGetValue(obj, out lodLevel)) lodLevel = -1; + + for(int k=0;k 0) + { + uv1 = vuv[indexA]; + uv2 = vuv[indexB]; + uv3 = vuv[indexC]; + } + + /*var uv31 = new Vector3(uv1.x, uv1.y, 0); + var uv32 = new Vector3(uv2.x, uv2.y, 0); + var uv33 = new Vector3(uv3.x, uv3.y, 0); + areaUV += Vector3.Cross(uv32 - uv31, uv33 - uv31).magnitude;*/ + + if (uv1.x < uvBounds.x) uvBounds.x = uv1.x; + if (uv1.y < uvBounds.y) uvBounds.y = uv1.y; + if (uv1.x > uvBounds.z) uvBounds.z = uv1.x; + if (uv1.y > uvBounds.w) uvBounds.w = uv1.y; + + if (uv2.x < uvBounds.x) uvBounds.x = uv2.x; + if (uv2.y < uvBounds.y) uvBounds.y = uv2.y; + if (uv2.x > uvBounds.z) uvBounds.z = uv2.x; + if (uv2.y > uvBounds.w) uvBounds.w = uv2.y; + + if (uv3.x < uvBounds.x) uvBounds.x = uv3.x; + if (uv3.y < uvBounds.y) uvBounds.y = uv3.y; + if (uv3.x > uvBounds.z) uvBounds.z = uv3.x; + if (uv3.y > uvBounds.w) uvBounds.w = uv3.y; + } + + // uv layouts always have empty spaces + //area /= areaUV; + + //var so = new SerializedObject(mr); + //var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue; + var scaleInLm = data.objToScaleInLm[obj]; + + if (!pstorage.alternativeScaleInLightmap) area *= scaleInLm; + + if (lmgroup.isImplicit && lodLevel == -1) + { + lmgroup.area += area; // accumulate LMGroup area + // only use base scene values, no LODs, to properly initialize autoatlas size + } + if (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas) + { + // Accumulate per-holder area and UV bounds + float existingArea; + Vector4 existingBounds; + holderObjUVBounds.TryGetValue(holderObj, out existingBounds); + if (!holderObjArea.TryGetValue(holderObj, out existingArea)) + { + existingArea = 0; + existingBounds = uvBounds; + List holderList; + if (!groupToHolderObjects.TryGetValue(lmgroup, out holderList)) + { + groupToHolderObjects[lmgroup] = holderList = new List(); + } + holderList.Add(holderObj); + } + holderObjArea[holderObj] = existingArea + area; + + existingBounds.x = existingBounds.x < uvBounds.x ? existingBounds.x : uvBounds.x; + existingBounds.y = existingBounds.y < uvBounds.y ? existingBounds.y : uvBounds.y; + existingBounds.z = existingBounds.z > uvBounds.z ? existingBounds.z : uvBounds.z; + existingBounds.w = existingBounds.w > uvBounds.w ? existingBounds.w : uvBounds.w; + holderObjUVBounds[holderObj] = existingBounds; + } + } + } + } + + static int ResolutionFromArea(float area) + { + int resolution = (int)(Mathf.Sqrt(area) * texelsPerUnit); + if (mustBePOT) + { + if (atlasCountPriority) + { + resolution = Mathf.NextPowerOfTwo(resolution); + } + else + { + resolution = Mathf.ClosestPowerOfTwo(resolution); + } + } + resolution = Math.Max(resolution, minAutoResolution); + resolution = Math.Min(resolution, maxAutoResolution); + + return resolution; + } + + static void CalculateAutoAtlasInitResolution(ExportSceneData data) + { + var groupList = data.groupList; + + // Calculate implicit lightmap resolution + for(int i=0; i holderObjs, ExportSceneData data) + { + var holderObjArea = data.holderObjArea; + var holderObjUVBounds = data.holderObjUVBounds; + + // Divide holders area to get from world space to -> UV space + float areaMult = 1.0f; + if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas) + { + // ...by maximum lightmap area given texel size (autoAtlas) + //areaMult = 1.0f / lightmapMaxArea; + // don't modify + } + else + { + // ... by maximum holder area (normalize) + float lmgroupArea = 0; + for(int i=0; i holderObjs, ExportSceneData data, PackData pdata) + { + var objToLodLevel = data.objToLodLevel; + var holderObjArea = data.holderObjArea; + var remainingAreaPerLodLevel = pdata.remainingAreaPerLodLevel; + + for(int i=0; i(); + if (comp != null && comp.instanceResolutionOverride) areaA = comp.instanceResolution * 10000; + + comp = b.GetComponent(); + if (comp != null && comp.instanceResolutionOverride) areaB = comp.instanceResolution * 10000; + + return areaB.CompareTo(areaA); + } + + static void ApplyAreaToUVBounds(float area, Vector4 uvbounds, out float width, out float height) + { + width = height = Mathf.Sqrt(area); + float uwidth = uvbounds.z - uvbounds.x; + float uheight = uvbounds.w - uvbounds.y; + if (uwidth == 0 && uheight == 0) + { + width = height = 0; + } + else + { + float uvratio = uheight / uwidth; + if (uvratio <= 1.0f) + { + width /= uvratio; + //height *= uvratio; + } + else + { + height *= uvratio; + //width /= uvratio; + } + } + } + + static bool Pack(BakeryLightmapGroup lmgroup, List holderObjs, ExportSceneData data, PackData pdata) + { + var holderObjArea = data.holderObjArea; + var holderObjUVBounds = data.holderObjUVBounds; + var holderRect = data.holderRect; + var objToLodLevel = data.objToLodLevel; + var groupList = data.groupList; + var lmBounds = data.lmBounds; + var autoAtlasGroups = data.autoAtlasGroups; + var autoAtlasGroupRootNodes = data.autoAtlasGroupRootNodes; + + var remainingAreaPerLodLevel = pdata.remainingAreaPerLodLevel; + + int atlasPaddingPixels = pstorage.texelPaddingForDefaultAtlasPacker; + + //Debug.LogError("repack: "+repackScale); + pdata.repack = false; + + AtlasNode rootNode; + + if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas && autoAtlasGroupRootNodes != null && autoAtlasGroupRootNodes.Count > 0) + { + rootNode = autoAtlasGroupRootNodes[0]; + } + else + { + rootNode = new AtlasNode(); + } + + rootNode.rc = new Rect(0, 0, 1, 1); + for(int i=0; i(); + if (comp != null && comp.instanceResolutionOverride) + { + // Explicit holder size + pdata.hasResOverrides = true; + width = height = comp.instanceResolution / (float)lmgroup.resolution; + } + else + { + // Automatic: width and height = sqrt(area) transformed by UV AABB aspect ratio + ApplyAreaToUVBounds(area, uvbounds, out width, out height); + + if (pstorage.alternativeScaleInLightmap) + { + var mr = GetValidRenderer(holderObjs[i]); + if (mr != null) + { + var so = new SerializedObject(mr); + var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue; + width *= scaleInLm; + height *= scaleInLm; + } + } + } + + // Clamp to full lightmap size + float twidth = width; + float theight = height; + if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas) + { + twidth = (width * texelsPerUnit) / lmgroup.resolution; + theight = (height * texelsPerUnit) / lmgroup.resolution; + + //if (i==0) Debug.LogError(texelsPerUnit+" "+twidth); + } + //float unclampedTwidth = twidth; + //float unclampedTheight = twidth; + if (comp != null && comp.instanceResolutionOverride) + { + } + else + { + twidth *= pdata.repackScale; + theight *= pdata.repackScale; + } + twidth = twidth > 1 ? 1 : twidth; + theight = theight > 1 ? 1 : theight; + twidth = Mathf.Max(twidth, 1.0f / lmgroup.resolution); + theight = Mathf.Max(theight, 1.0f / lmgroup.resolution); + var rect = new Rect(0, 0, twidth, theight); + + if (float.IsNaN(twidth) || float.IsNaN(theight)) + { + ExportSceneError("NaN UVs detected for " + holderObjs[i].name+" "+rect.width+" "+rect.height+" "+width+" "+height+" "+lmgroup.resolution+" "+area+" "+(uvbounds.z - uvbounds.x)+" "+(uvbounds.w - uvbounds.y)); + return false; + } + + // Try inserting this rect + // Break autoatlas if lod level changes + // Optionally break autoatlas if scene changes + AtlasNode node = null; + int lodLevel; + if (!objToLodLevel.TryGetValue(holderObjs[i], out lodLevel)) lodLevel = -1; + bool splitAtlas = false; + if (splitByScene) + { + if (holderObjs[i].scene.name != lmgroup.sceneName) + { + splitAtlas = true; + } + } + if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap) + { + bool ba = holderObjs[i].name == "__ExportTerrainParent"; + if (ba) lmgroup.containsTerrains = true; + + if (i > 0) + { + bool bb = holderObjs[i-1].name == "__ExportTerrainParent"; + if (ba != bb) + { + splitAtlas = true; + } + } + } + if (!splitAtlas) + { + if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas) + { + if (lodLevel == lmgroup.sceneLodLevel) + { + node = rootNode.Insert(holderObjs[i], rect); + } + } + else + { + node = rootNode.Insert(holderObjs[i], rect); + } + } + + /*if (node!=null) + { + Debug.Log(holderObjs[i].name+" goes straight into "+lmgroup.name); + }*/ + + if (node == null) + { + if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas) + { + // Can't fit - try other autoAtlas lightmaps + BakeryLightmapGroup newGroup = null; + var holder = holderObjs[i]; + int goodGroup = -1; + for(int g=1; g 1 ? 1 : twidth; + theight = theight > 1 ? 1 : theight; + rect = new Rect(0, 0, twidth, theight); + + node = autoAtlasGroupRootNodes[g].Insert(holder, rect); + if (node != null) + { + //Debug.Log(holder.name+" fits into "+autoAtlasGroups[g].name); + newGroup = autoAtlasGroups[g]; + goodGroup = g; + break; + } + } + + // Can't fit - create new lightmap (autoAtlas) + if (goodGroup < 0) + { + newGroup = ScriptableObject.CreateInstance(); + newGroup.name = holder.scene.name + "_LMA" + autoAtlasGroups.Count; + newGroup.isImplicit = true; + newGroup.sceneLodLevel = lodLevel; + if (splitByScene) newGroup.sceneName = holderObjs[i].scene.name; + //Debug.Log(holder.name+" creates "+newGroup.name); + + if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap) + { + newGroup.containsTerrains = holderObjs[i].name == "__ExportTerrainParent"; + } + + newGroup.resolution = (int)(Mathf.Sqrt(remainingAreaPerLodLevel[lodLevel]) * texelsPerUnit); + if (mustBePOT) + { + if (atlasCountPriority) + { + newGroup.resolution = Mathf.NextPowerOfTwo(newGroup.resolution); + } + else + { + newGroup.resolution = Mathf.ClosestPowerOfTwo(newGroup.resolution); + } + } + newGroup.resolution = Math.Max(newGroup.resolution, minAutoResolution); + newGroup.resolution = Math.Min(newGroup.resolution, maxAutoResolution); + + newGroup.bitmask = 1; + newGroup.area = 0; + newGroup.mode = BakeryLightmapGroup.ftLMGroupMode.PackAtlas; + + newGroup.id = data.lmid; + groupList.Add(newGroup); + lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0))); + data.lmid++; + + autoAtlasGroups.Add(newGroup); + var rootNode2 = new AtlasNode(); + rootNode2.rc = new Rect(0, 0, 1, 1); + autoAtlasGroupRootNodes.Add(rootNode2); + + twidth = (width * texelsPerUnit) / newGroup.resolution; + theight = (height * texelsPerUnit) / newGroup.resolution; + //unclampedTwidth = twidth; + //unclampedTheight = twidth; + twidth = twidth > 1 ? 1 : twidth; + theight = theight > 1 ? 1 : theight; + + rect = new Rect(0, 0, twidth, theight); + + node = rootNode2.Insert(holder, rect); + } + + // Modify implicit group storage + MoveObjectToImplicitGroup(holder, newGroup, data); + /* + var scn = holder.scene; + tempStorage.implicitGroupMap[holder] = newGroup; + for(int k=0; k atlas UV + float padding = ((float)atlasPaddingPixels) / lmgroup.resolution; + + var paddedRc = new Rect(node.rc.x + padding, + node.rc.y + padding, + node.rc.width - padding * 2, + node.rc.height - padding * 2); + + paddedRc.x -= uvbounds.x * (paddedRc.width / (uvbounds.z - uvbounds.x)); + paddedRc.y -= uvbounds.y * (paddedRc.height / (uvbounds.w - uvbounds.y)); + paddedRc.width /= uvbounds.z - uvbounds.x; + paddedRc.height /= uvbounds.w - uvbounds.y; + + holderRect[holderObjs[i]] = paddedRc; + } + + //float areaReduction = (twidth*theight) / (unclampedTwidth*unclampedTheight); + remainingAreaPerLodLevel[lodLevel] -= area;// * areaReduction; + } + + if (!lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas) + { + if (pdata.finalRepack && pdata.repack) + { + pdata.continueRepack = true; + return true; + } + if (pdata.finalRepack) + { + pdata.continueRepack = false; + return true; + } + + if (!pdata.repack && !pdata.repackStage2) + { + //if (repackTries > 0) break; // shrinked down just now - don't scale up + + pdata.repackStage2 = true; // scale up now + pdata.repack = true; + pdata.repackScale *= atlasScaleUpValue;///= 0.75f; + pdata.repackTries = 0; + //Debug.LogError("Scale up, set " +repackScale); + } + else if (pdata.repackStage2) + { + pdata.repackTries++; + if (pdata.repackTries == atlasMaxTries) + { + pdata.continueRepack = false; + return true; + } + pdata.repack = true; + pdata.repackScale *= atlasScaleUpValue;///= 0.75f; + //Debug.LogError("Scale up cont, set " +repackScale); + } + } + return true; + } + + static void MoveObjectToImplicitGroup(GameObject holder, BakeryLightmapGroup newGroup, ExportSceneData data) + { + var storages = data.storages; + var sceneToID = data.sceneToID; + + // Modify implicit group storage + var scn = holder.scene; + tempStorage.implicitGroupMap[holder] = newGroup; + for(int k=0; k GetAtlasBucketRanges(List holderObjs, ExportSceneData data, bool onlyUserSplits) + { + var objToLodLevel = data.objToLodLevel; + + var ranges = new List(); + int start = 0; + int end = 0; + if (holderObjs.Count > 0) + { + var sceneName = holderObjs[0].scene.name; + int lodLevel; + if (!objToLodLevel.TryGetValue(holderObjs[0], out lodLevel)) lodLevel = -1; + bool isTerrain = holderObjs[0].name == "__ExportTerrainParent"; + + for(int i=0; i holderObjs, int start, int end, ExportSceneData data) + { + var holderObjArea = data.holderObjArea; + + float area = 0; + for(int i=start; i<=end; i++) + { + area += holderObjArea[holderObjs[i]]; + } + return area; + } + + static BakeryLightmapGroup AllocateAutoAtlas(int count, BakeryLightmapGroup lmgroup, ExportSceneData data, int[] atlasSizes = null) + { + var lmBounds = data.lmBounds; + var groupList = data.groupList; + var autoAtlasGroups = data.autoAtlasGroups; + var autoAtlasGroupRootNodes = data.autoAtlasGroupRootNodes; + + BakeryLightmapGroup newGroup = null; + + for(int i=0; i(); + newGroup.name = lmgroup.sceneName + "_LMA" + autoAtlasGroups.Count; + newGroup.isImplicit = true; + newGroup.sceneLodLevel = lmgroup.sceneLodLevel; + if (splitByScene) newGroup.sceneName = lmgroup.sceneName; + newGroup.containsTerrains = lmgroup.containsTerrains; + + newGroup.resolution = atlasSizes != null ? atlasSizes[i] : lmgroup.resolution; + + newGroup.bitmask = 1; + newGroup.area = 0; + newGroup.mode = lmgroup.mode;// BakeryLightmapGroup.ftLMGroupMode.PackAtlas; + + newGroup.renderMode = lmgroup.renderMode; + newGroup.renderDirMode = lmgroup.renderDirMode; + newGroup.atlasPacker = lmgroup.atlasPacker; + newGroup.computeSSS = lmgroup.computeSSS; + newGroup.sssSamples = lmgroup.sssSamples; + newGroup.sssDensity = lmgroup.sssDensity; + newGroup.sssColor = lmgroup.sssColor * lmgroup.sssScale; + newGroup.fakeShadowBias = lmgroup.fakeShadowBias; + newGroup.transparentSelfShadow = lmgroup.transparentSelfShadow; + newGroup.flipNormal = lmgroup.flipNormal; + + newGroup.id = data.lmid; + groupList.Add(newGroup); + lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0))); + data.lmid++; + + autoAtlasGroups.Add(newGroup); + var rootNode2 = new AtlasNode(); + rootNode2.rc = new Rect(0, 0, 1, 1); + autoAtlasGroupRootNodes.Add(rootNode2); + } + + return newGroup; + } + + static bool PackWithXatlas(BakeryLightmapGroup lmgroup, List holderObjs, ExportSceneData data, PackData pdata) + { + var holderObjArea = data.holderObjArea; + var holderObjUVBounds = data.holderObjUVBounds; + var holderRect = data.holderRect; + var autoAtlasGroups = data.autoAtlasGroups; + var objToLodLevel = data.objToLodLevel; + var objsToWriteHolder = data.objsToWriteHolder; + var objsToWrite = data.objsToWrite; + + bool warned = false; + + // Split objects into "buckets" by scene, terrains, LODs, etc + // Objects are already pre-sorted, so we need only ranges + int bStart = 0; + int bEnd = holderObjs.Count-1; + int bucketCount = 2; + List buckets = null; + if (lmgroup.isImplicit) + { + buckets = GetAtlasBucketRanges(holderObjs, data, postPacking); + bucketCount = buckets.Count; + } + + var holderAutoIndex = new int[holderObjs.Count]; + + for(int bucket=0; bucket 0) + { + // Start new bucket + lmgroup = AllocateAutoAtlas(1, lmgroup, data); + } + int firstAutoAtlasIndex = autoAtlasGroups.Count - 1; + if (autoAtlasGroups.Count > 0 && autoAtlasGroups[0] == lmgroup) + { + // new bucket always uses the original LMGroup + // if the original LMGroup is implicit, it'll be moved to firstAutoAtlasIndex + // but in case of the actual autoAtlas, the original LMGroup should use atlas index 0 + firstAutoAtlasIndex = 0; + } + + if (lmgroup.isImplicit) + { + float bucketArea = SumObjectsArea(holderObjs, bStart, bEnd, data); + lmgroup.resolution = ResolutionFromArea(bucketArea); + } + + // Fill some LMGroup data + lmgroup.sceneName = holderObjs[bStart].scene.name; + int lodLevel; + if (!objToLodLevel.TryGetValue(holderObjs[bStart], out lodLevel)) lodLevel = -1; + lmgroup.sceneLodLevel = lodLevel; + if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap) + { + lmgroup.containsTerrains = holderObjs[bStart].name == "__ExportTerrainParent"; + } + + var atlas = xatlas.xatlasCreateAtlas(); + + const int attempts = 4096; + int padding = pstorage.texelPaddingForXatlasAtlasPacker; + const bool allowRotate = false; + float packTexelsPerUnit = lmgroup.isImplicit ? 1.0f : 0.0f; // multiple atlaseses vs single atlas + int packResolution = lmgroup.resolution; + int maxChartSize = 0;//packResolution; + bool bruteForce = true; // high quality + + int vertCount = 4; + int indexCount = 6; + Vector2[] uv = null; + int[] indices = null; + if (!holeFilling) + { + uv = new Vector2[4]; + indices = new int[6]; + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 2; + indices[4] = 3; + indices[5] = 0; + } + var uvBuffer = new Vector2[4]; + var xrefBuffer = new int[4]; + var indexBuffer = new int[6]; + + for(int i=bStart; i<=bEnd; i++) + { + if (!warned) + { + var comp = holderObjs[i].GetComponent(); + if (comp != null && comp.instanceResolutionOverride) + { + if (!ExportSceneValidationMessage("When using xatlas as atlas packer, 'Override resolution' option is not supported for LMGroups.\nOption is used on: " + holderObjs[i].name)) + { + xatlas.xatlasClear(atlas); + return false; + } + warned = true; + } + } + + var area = holderObjArea[holderObjs[i]]; + var uvbounds = holderObjUVBounds[holderObjs[i]]; + + // Automatic: width and height = sqrt(area) transformed by UV AABB aspect ratio + float width, height; + ApplyAreaToUVBounds(area, uvbounds, out width, out height); + + // Clamp to full lightmap size + float twidth = width; + float theight = height; + if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas) + { + twidth = (width * texelsPerUnit);// / lmgroup.resolution; + theight = (height * texelsPerUnit);// / lmgroup.resolution; + } + + if (!holeFilling) + { + uv[0] = new Vector2(0,0); + uv[1] = new Vector2(twidth,0); + uv[2] = new Vector2(twidth,theight); + uv[3] = new Vector2(0,theight); + } + else + { + List indexList = null; + List uvList = null; + vertCount = indexCount = 0; + int numMeshes = 0; + var ubounds = holderObjUVBounds[holderObjs[i]]; + var holder = holderObjs[i]; + for(int o=0; o(); + uvList = new List(); + for(int j=0; j 0) + { + for(int j=0; j 1) + { + uv = uvList.ToArray(); + indices = indexList.ToArray(); + } + } + + var handleUV = GCHandle.Alloc(uv, GCHandleType.Pinned); + int err = 0; + + try + { + var pointerUV = handleUV.AddrOfPinnedObject(); + + err = xatlas.xatlasAddUVMesh(atlas, vertCount, pointerUV, indexCount, indices, allowRotate); + } + finally + { + if (handleUV.IsAllocated) handleUV.Free(); + } + + if (err == 1) + { + Debug.LogError("xatlas::AddMesh: indices are out of range"); + xatlas.xatlasClear(atlas); + return false; + } + else if (err == 2) + { + Debug.LogError("xatlas::AddMesh: index count is incorrect"); + xatlas.xatlasClear(atlas); + return false; + } + else if (err != 0) + { + Debug.LogError("xatlas::AddMesh: unknown error"); + xatlas.xatlasClear(atlas); + return false; + } + } + + //xatlas.xatlasParametrize(atlas); + xatlas.xatlasPack(atlas, attempts, packTexelsPerUnit, packResolution, maxChartSize, padding, bruteForce, true);//, allowRotate); + + int atlasCount = xatlas.xatlasGetAtlasCount(atlas); + var atlasSizes = new int[atlasCount]; + + xatlas.xatlasNormalize(atlas, atlasSizes); + + // Create additional lightmaps + AllocateAutoAtlas(atlasCount-1, lmgroup, data, atlasSizes); + + // Move objects into new atlases + if (lmgroup.isImplicit) + { + for(int i=0; i<=bSize; i++) + { + int atlasIndex = xatlas.xatlasGetAtlasIndex(atlas, i, 0); + + // Modify implicit group storage + var holder = holderObjs[bStart + i]; + var newGroup = autoAtlasGroups[firstAutoAtlasIndex + atlasIndex]; + MoveObjectToImplicitGroup(holderObjs[bStart + i], newGroup, data); + holderAutoIndex[bStart + i] = firstAutoAtlasIndex + atlasIndex; + } + } + + for(int i=0; i<=bSize; i++) + { + // Get data from xatlas + int newVertCount = xatlas.xatlasGetVertexCount(atlas, i); + uvBuffer = new Vector2[newVertCount]; + xrefBuffer = new int[newVertCount]; + + int newIndexCount = xatlas.xatlasGetIndexCount(atlas, i); + indexBuffer = new int[newIndexCount]; + + if (holeFilling) + { + uvBuffer = new Vector2[newVertCount]; + xrefBuffer = new int[newVertCount]; + indexBuffer = new int[newIndexCount]; + } + + var handleT = GCHandle.Alloc(uvBuffer, GCHandleType.Pinned); + var handleX = GCHandle.Alloc(xrefBuffer, GCHandleType.Pinned); + var handleI = GCHandle.Alloc(indexBuffer, GCHandleType.Pinned); + try + { + var pointerT = handleT.AddrOfPinnedObject(); + var pointerX = handleX.AddrOfPinnedObject(); + var pointerI = handleI.AddrOfPinnedObject(); + xatlas.xatlasGetData(atlas, i, pointerT, pointerX, pointerI); + } + finally + { + if (handleT.IsAllocated) handleT.Free(); + if (handleX.IsAllocated) handleX.Free(); + if (handleI.IsAllocated) handleI.Free(); + } + + float minU = float.MaxValue; + float minV = float.MaxValue; + float maxU = -float.MaxValue; + float maxV = -float.MaxValue; + for(int j=0; j maxU) maxU = uvBuffer[j].x; + if (uvBuffer[j].y > maxV) maxV = uvBuffer[j].y; + } + + // Generate final rectangle to transform local UV -> atlas UV + float upadding = 0; + var uvbounds = holderObjUVBounds[holderObjs[bStart + i]]; + var paddedRc = new Rect(minU + upadding, + minV + upadding, + (maxU-minU) - upadding * 2, + (maxV-minV) - upadding * 2); + + paddedRc.x -= uvbounds.x * (paddedRc.width / (uvbounds.z - uvbounds.x)); + paddedRc.y -= uvbounds.y * (paddedRc.height / (uvbounds.w - uvbounds.y)); + paddedRc.width /= uvbounds.z - uvbounds.x; + paddedRc.height /= uvbounds.w - uvbounds.y; + + holderRect[holderObjs[bStart + i]] = paddedRc; + } + + xatlas.xatlasClear(atlas); + } + + if (postPacking) + { + buckets = GetAtlasBucketRanges(holderObjs, data, false); + bucketCount = buckets.Count; + + DebugLogInfo("Bucket count for " + lmgroup.name +": " + (bucketCount/2)); + + if (lmgroup.isImplicit) + { + // Post-packing for auto-atlased groups + var autoLMBuckets = new List[autoAtlasGroups.Count]; + for(int bucket=0; bucket(); + if (!autoLMBuckets[autoLM].Contains(bucket)) autoLMBuckets[autoLM].Add(bucket); + } + } + int origGroupCount = autoAtlasGroups.Count; + for(int i=0; i 1) + { + // Split + for(int j=1; j "+newGroup.name + " (" + newGroup.id+", "+newGroup.parentID+")"); + + for(int k=bStart; k<=bEnd; k++) + { + int autoLM = holderAutoIndex[k]; + if (autoLM == i) + { + MoveObjectToImplicitGroup(holderObjs[k], newGroup, data); + holderAutoIndex[k] = -1; // mark as moved + } + } + } + } + } + for(int i=0; i 0) + { + // Post-packing for explicit groups + // Single LMGroup -> LMGroup*buckets + + // Setup first bucket + bStart = buckets[0]; + bEnd = buckets[1]; + int lodLevel; + if (!objToLodLevel.TryGetValue(holderObjs[bStart], out lodLevel)) lodLevel = -1; + lmgroup.sceneLodLevel = lodLevel; + if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap) + { + lmgroup.containsTerrains = holderObjs[bStart].name == "__ExportTerrainParent"; + } + //Debug.LogError(lmgroup.name+": "+ lmgroup.sceneLodLevel+" because of " + holderObjs[bStart].name); + + // Skip first bucket + for(int bucket=2; bucket holderObjs, ExportSceneData data, PackData pdata) + { + var holderRect = data.holderRect; + + if (!lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas && !pdata.hasResOverrides) + { + float maxx = 0; + float maxy = 0; + for(int i=0; i maxx) maxx = rect.x + rect.width; + if ((rect.y + rect.height) > maxy) maxy = rect.y + rect.height; + } + float maxDimension = maxx > maxy ? maxx : maxy; + float normalizeScale = 1.0f / maxDimension; + for(int i=0; i(); + for(int g=0; g 0) continue; + + var rootNode = autoAtlasGroupRootNodes[g]; + float maxx = 0; + float maxy = 0; + rootNode.GetMax(ref maxx, ref maxy); + float maxDimension = maxx > maxy ? maxx : maxy; + float normalizeScale = 1.0f / maxDimension; + stack.Clear(); + stack.Push(rootNode); + while(stack.Count > 0) + { + var node = stack.Pop(); + if (node.obj != null) + { + var rect = holderRect[node.obj]; + holderRect[node.obj] = new Rect(rect.x * normalizeScale, rect.y * normalizeScale, rect.width * normalizeScale, rect.height * normalizeScale); + } + if (node.child0 != null) stack.Push(node.child0); + if (node.child1 != null) stack.Push(node.child1); + } + if (maxDimension < 0.5f) + { + lmgroup.resolution /= 2; // shrink the lightmap after normalization if it was too empty + lmgroup.resolution = Math.Max(lmgroup.resolution, minAutoResolution); + } + } + } + + static void JoinAutoAtlases(ExportSceneData data) + { + var autoAtlasGroups = data.autoAtlasGroups; + var autoAtlasGroupRootNodes = data.autoAtlasGroupRootNodes; + var groupList = data.groupList; + var lmBounds = data.lmBounds; + var holderRect = data.holderRect; + var objsToWrite = data.objsToWrite; + var objsToWriteGroup = data.objsToWriteGroup; + + var stack = new Stack(); + + // Join autoatlases + var autoAtlasCategories = new List(); + bool joined = false; + for(int g=0; g 0) continue; + + string cat = "/" + autoAtlasGroups[g].sceneLodLevel; + if (splitByScene) cat = autoAtlasGroups[g].sceneName + cat; + if (!autoAtlasCategories.Contains(cat)) autoAtlasCategories.Add(cat); + } + for(int alod=0; alod(); + var atlasStack = new Stack(); + for(int g=0; g 0) continue; + + var thisCat = "/" + autoAtlasGroups[g].sceneLodLevel; + if (splitByScene) thisCat = autoAtlasGroups[g].sceneName + thisCat; + + if (thisCat != cat) continue; + if (autoAtlasGroups[g].resolution == maxAutoResolution) continue; + if (!autoAtlasSizes.Contains(autoAtlasGroups[g].resolution)) autoAtlasSizes.Add(autoAtlasGroups[g].resolution); + } + autoAtlasSizes.Sort(); + for(int s=0; s 0) continue; + + var thisCat = "/" + autoAtlasGroups[g].sceneLodLevel; + if (splitByScene) thisCat = autoAtlasGroups[g].sceneName + thisCat; + + if (thisCat != cat) continue; + if (autoAtlasGroups[g].resolution != asize) continue; + atlasStack.Push(autoAtlasGroups[g]); + if (atlasStack.Count == 4) + { + var newGroup = ScriptableObject.CreateInstance(); + newGroup.name = autoAtlasGroups[g].name; + newGroup.isImplicit = true; + newGroup.sceneLodLevel = autoAtlasGroups[g].sceneLodLevel; + newGroup.sceneName = autoAtlasGroups[g].sceneName; + + newGroup.resolution = asize * 2; + + newGroup.bitmask = autoAtlasGroups[g].bitmask; + newGroup.mode = BakeryLightmapGroup.ftLMGroupMode.PackAtlas; + + newGroup.id = data.lmid; + groupList.Add(newGroup); + lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0))); + data.lmid++; + + autoAtlasGroups.Add(newGroup); + var rootNode2 = new AtlasNode(); + rootNode2.rc = new Rect(0, 0, 1, 1); + autoAtlasGroupRootNodes.Add(rootNode2); + + // Top + rootNode2.child0 = new AtlasNode(); + rootNode2.child0.rc = new Rect(0, 0, 1, 0.5f); + + // Bottom + rootNode2.child1 = new AtlasNode(); + rootNode2.child1.rc = new Rect(0, 0.5f, 1, 0.5f); + + for(int gg=0; gg<4; gg++) + { + var subgroup = atlasStack.Pop(); + var id = autoAtlasGroups.IndexOf(subgroup); + var subgroupRootNode = autoAtlasGroupRootNodes[id]; + float ox, oy, sx, sy; + + if (gg == 0) + { + // Left top + rootNode2.child0.child0 = subgroupRootNode; + //rootNode2.child0.child0.Transform(0, 0, 0.5f, 0.5f); + //offsetScale = rootNode2.child0.child0.rc; + ox = 0; oy = 0; sx = 0.5f; sy = 0.5f; + } + else if (gg == 1) + { + // Right top + rootNode2.child0.child1 = subgroupRootNode; + //rootNode2.child0.child1.Transform(0.5f, 0, 0.5f, 0.5f); + //offsetScale = rootNode2.child0.child1.rc; + ox = 0.5f; oy = 0; sx = 0.5f; sy = 0.5f; + } + else if (gg == 2) + { + // Left bottom + rootNode2.child1.child0 = subgroupRootNode; + //rootNode2.child1.child0.Transform(0, 0.5f, 0.5f, 0.5f); + //offsetScale = rootNode2.child1.child0.rc; + ox = 0; oy = 0.5f; sx = 0.5f; sy = 0.5f; + } + else + { + // Right bottom + rootNode2.child1.child1 = subgroupRootNode; + //rootNode2.child1.child1.Transform(0.5f, 0.5f, 0.5f, 0.5f); + //offsetScale = rootNode2.child1.child1.rc; + ox = 0.5f; oy = 0.5f; sx = 0.5f; sy = 0.5f; + } + + autoAtlasGroups.RemoveAt(id); + autoAtlasGroupRootNodes.RemoveAt(id); + + id = groupList.IndexOf(subgroup); + groupList.RemoveAt(id); + lmBounds.RemoveAt(id); + + for(int x=id; x 0) + { + var node = stack.Pop(); + if (node.obj != null) + { + var rect = holderRect[node.obj]; + holderRect[node.obj] = new Rect(rect.x * sx + ox, + rect.y * sy + oy, + rect.width * sx, + rect.height * sy); + + MoveObjectToImplicitGroup(node.obj, newGroup, data); + + /* + tempStorage.implicitGroupMap[node.obj] = newGroup; + for(int k=0; k sceneToID = new Dictionary(); + public Dictionary sceneHasStorage = new Dictionary(); + + // Object properties + public Dictionary objToLodLevel = new Dictionary(); // defines atlas LOD level + public Dictionary> objToLodLevelVisible = new Dictionary>(); // defines LOD levels where this object is visible + public Dictionary objToScaleInLm = new Dictionary(); + + public List objsToWrite = new List(); + public List objsToWriteLightmapped = new List(); + public List objsToWriteGroup = new List(); + public List objsToWriteHolder = new List(); + public List objsToWriteScaleOffset = new List(); + public List objsToWriteUVOverride = new List(); + public List objsToWriteNames = new List(); + public List objsToWriteVerticesPosW = new List(); + public List objsToWriteVerticesNormalW = new List(); + public List objsToWriteVerticesTangentW = new List(); + public List objsToWriteVerticesUV = new List(); + public List objsToWriteVerticesUV2 = new List(); + public List objsToWriteIndices = new List(); + public List objsToWriteHasMetaAlpha = new List(); + + public List outsideRenderers = new List(); // for sector+SRP only + + // Auto-atlasing + public List autoAtlasGroups = new List(); + public List autoAtlasGroupRootNodes = new List(); + public BakeryLightmapGroup autoVertexGroup; + + // Data to collect for atlas packing + public Dictionary holderObjArea = new Dictionary(); // LMGroup holder area, accumulated from all children + public Dictionary holderObjUVBounds = new Dictionary(); // LMGroup holder 2D UV AABB + public Dictionary> groupToHolderObjects = new Dictionary>(); // LMGroup -> holders map + public Dictionary holderRect = new Dictionary(); + + // Per-LMGroup data + public List groupList = new List(); + public List lmBounds = new List(); // list of bounding boxes around LMGroups for testing lights + + // Geometry data + public List[] indicesOpaqueLOD = null; + public List[] indicesTransparentLOD = null; + + public int lmid = 0; // LMID counter + + public ExportSceneData(int sceneCount) + { + storages = new ftLightmapsStorage[sceneCount]; + } + } + + class AdjustUVPaddingData + { + public List dirtyObjList = new List(); + public List dirtyAssetList = new List(); + public Dictionary> meshToObjIDs = new Dictionary>(); + public Dictionary meshToPaddingMap = new Dictionary(); + } + + class PackData + { + public Dictionary remainingAreaPerLodLevel = new Dictionary(); + public bool repack = true; + public bool repackStage2 = false; + public bool finalRepack = false; + public float repackScale = 1; + public int repackTries = 0; + public bool hasResOverrides = false; + public bool continueRepack = false; + } + + static public IEnumerator ExportScene(EditorWindow window, bool renderTextures = true, bool atlasOnly = false, BakerySectorCapture sectorCaptureAsset = null) + { + lmgroupHolder = null; // important to properly flush previous scale-in-lightmaps + + userCanceled = false; + ProgressBarInit("Exporting scene - preparing...", window); + yield return null; + + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + gstorage = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftGlobalStorage.asset", typeof(ftGlobalStorage)) as ftGlobalStorage; + pstorage = ftLightmaps.GetProjectSettings(); + + bool isDX11 = SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11; + bool nonDX11 = !isDX11; + + bool _unwrapUVs = unwrapUVs; + bool _forceDisableUnwrapUVs = forceDisableUnwrapUVs; + if (ftRenderLightmap.fullSectorRender && !ftRenderLightmap.curSector.allowUVPaddingAdjustment) + { + _unwrapUVs = false; + _forceDisableUnwrapUVs = false; + } + + var time = GetTime(); + var ms = time; + var startMsU = ms; + double totalTime = GetTime(); + double vbTimeRead = 0; + double vbTimeWrite = 0; + double vbTimeWriteFull = 0; + double vbTimeWriteT = 0; + double vbTimeWriteT2 = 0; + double vbTimeWriteT3 = 0; + double ibTime = 0; + var sceneCount = SceneManager.sceneCount; + var indicesOpaque = new List(); + var indicesTransparent = new List(); + + var data = new ExportSceneData(sceneCount); + + bool tangentSHLights = CheckForTangentSHLights(); + + // Per-LMGroup data + var lmAlbedoList = new List(); // list of albedo texture for UV GBuffer rendering + var lmAlbedoListTex = new List(); + var lmAlphaList = new List(); // list of alpha textures for alpha buffer generation + var lmAlphaListRAM = new List(); // non-DX11 array + var lmAlphaListTex = new List(); + var lmAlphaRefList = new List(); // list of alpha texture refs + var lmAlphaChannelList = new List(); // list of alpha channels + + // lod-related + var lmVOffset = new List(); + var lmUVArrays = new List>(); + var lmUVArrays2 = new List(); + var lmUVArrays3 = new List(); + var lmIndexArrays = new List>(); + var lmIndexArrays2 = new List(); + var lmLocalToGlobalIndices = new List>(); + + vbtraceTexPosNormalArray = new List(); + vbtraceTexUVArray = new List(); + + sceneLodsUsed = 0; + + // Create temp path + CreateSceneFolder(); + + // Disable preview of any sectors + if (sectorCaptureAsset == null) + { + var allSectors = FindObjectsOfType(typeof(BakerySector)) as BakerySector[]; + for(int i=0; i(); + terrainObjectToActual = new List(); + terrainObjectToHeightMap = new List(); + terrainObjectToHeightMapRAM = new List(); + terrainObjectToBounds = new List(); + terrainObjectToBoundsUV = new List(); + terrainObjectToFlags = new List(); + terrainObjectToLMID = new List(); + terrainObjectToHeightMips = new List>(); + temporaryGameObjects = new List(); + temporaryAreaLightMeshList = new List(); + temporaryAreaLightMeshList2 = new List(); + + var objects = Resources.FindObjectsOfTypeAll(typeof(GameObject)); + //var objects = UnityEngine.Object.FindObjectsOfTypeAll(typeof(GameObject)); + + try + { + ms = GetTime(); + + //if (!onlyUVdata) + //{ + time = ms; + + // Get manually created LMGroups + CollectExplicitLMGroups(data); + + // Object conversion loop / also validate for multiple scene storages + for(int objNum = 0; objNum < objects.Length; objNum++) + { + GameObject obj = (GameObject)objects[objNum]; + if (obj == null) continue; + if (!CheckForMultipleSceneStorages(obj, data)) yield break; + if (ConvertUnityAreaLight(obj)) continue; + ConvertTerrain(obj); + } + + // Regather objects if new were added + if (terrainObjectList.Count > 0 || temporaryGameObjects.Count > 0 || temporaryAreaLightMeshList.Count > 0) + { + //objects = UnityEngine.Object.FindObjectsOfTypeAll(typeof(GameObject)); + objects = Resources.FindObjectsOfTypeAll(typeof(GameObject)); + } + + tempStorage.implicitGroupMap = new Dictionary(); // implicit holder -> LMGroup map. used by GetLMGroupFromObject + + // Find LODGroups -> LODs -> scene-wide LOD distances + // Map objects to scene-wide LOD levels + MapObjectsToSceneLODs(data, objects); + + ftModelPostProcessor.Init(); + + // Filter objects, convert to property arrays + if (!FilterObjects(data, objects)) yield break; + + if (ftRenderLightmap.fullSectorRender) + { + var sector = ftRenderLightmap.curSector; + if (sector.captureMode == BakerySector.CaptureMode.CaptureInPlace || (sectorCaptureAsset != null && sectorCaptureAsset.write)) + { + int objCount = data.objsToWrite.Count; + var cpoints = sector.cpoints; + + // Render albedo/alpha/depth for each sector point + var fdata = new FarSphereRenderData[cpoints.Count]; + for(int i=0; i(); + sectorCaptureAsset.positions = new List(); + sectorCaptureAsset.textures = new List(); + for(int i=0; i(); + atlasOnlySize = new List(); + atlasOnlyID = new List(); + atlasOnlyScaleOffset = new List(); + var emptyVec4 = new Vector4(1,1,0,0); + Rect rc = new Rect(); + for(int i=0; i(); + for(int i=0; i(); + for(int o=0; o 0 && ftRenderLightmap.verbose) + { + ProgressBarEnd(false); + if (!EditorUtility.DisplayDialog("Lightmap overwrite", "These lightmaps will be overwritten:\n\n" + existingFilenames, "Overwrite", "Cancel")) + { + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + ProgressBarInit("Exporting scene - preparing...", window); + } + } + + ftRenderLightmap.giLodModeEnabled = ftRenderLightmap.giLodMode == ftRenderLightmap.GILODMode.ForceOn; + ulong approxMem = 0; + + if (groupList.Count > 100 && ftRenderLightmap.verbose) + { + ProgressBarEnd(false); + if (!EditorUtility.DisplayDialog("Lightmap count check", groupList.Count + " lightmaps are going to be rendered. Continue?", "Continue", "Cancel")) + { + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + ProgressBarInit("Exporting scene - preparing...", window); + } + + if (memoryWarning || ftRenderLightmap.giLodMode == ftRenderLightmap.GILODMode.Auto) + { + for(int i=0; i SystemInfo.graphicsMemorySize) + { + DebugLogInfo("GI VRAM auto optimization ON: estimated usage " + (int)approxMem + " > " + SystemInfo.graphicsMemorySize); + ftRenderLightmap.giLodModeEnabled = true; + } + else + { + DebugLogInfo("GI VRAM auto optimization OFF: estimated usage " + (int)approxMem + " < " + SystemInfo.graphicsMemorySize); + } + } + + // Generate terrain geometry with detail enough for given size for UVGBuffer purposes + fhmaps = new BinaryWriter(File.Open(scenePath + "/heightmaps.bin", FileMode.Create)); + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add("heightmaps.bin"); + if (exportTerrainAsHeightmap) + { + for(int i=0; i 0) + { + for(int i=0; i 0) uvgbGlobalFlags |= UVGBFLAG_TERRAIN; + SetUVGBFlags(uvgbGlobalFlags); + + for(int i=0; i 8192) DebugLogWarning("Warning: vertex lightmap group " + lmgroup.name + " uses resolution of " + atlasTexSize); + atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)ftRenderLightmap.tileSize) * ftRenderLightmap.tileSize; + flms.Write(-atlasTexSize); + } + else + { + flms.Write(lmgroup.resolution); + } + //Debug.LogError(lmgroup.name+": " + lmgroup.resolution); + } + flms.Close(); + flmlod.Close(); + flmuvgb.Close(); + + voffset = ioffset = soffset = 0; // vertex/index/surface write + + // Per-surface alpha texture IDs + var alphaIDs = new List(); + + int albedoCounter = 0; + var albedoMap = new Dictionary(); // albedo ptr -> ID map + + int alphaCounter = 0; + var alphaMap = new Dictionary>(); // alpha ptr -> ID map + + var dummyTexList = new List(); // list of single-color 1px textures + var dummyPixelArray = new Color[1]; + + if (ftRenderLightmap.checkOverlaps) + { + var quad = GameObject.CreatePrimitive(PrimitiveType.Quad); + var plane = GameObject.CreatePrimitive(PrimitiveType.Plane); + var qmesh = quad.GetComponent().sharedMesh; + var pmesh = plane.GetComponent().sharedMesh; + DestroyImmediate(quad); + DestroyImmediate(plane); + bool canCheck = ftModelPostProcessor.InitOverlapCheck(); + if (!canCheck) + { + DebugLogError("Can't load ftOverlapTest.shader"); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + for(int g=0; g 1.0001f || usedUVs[v].y < -0.0001f || usedUVs[v].y > 1.0001f) + { + validUVs = false; + break; + } + } + if (!validUVs && ftRenderLightmap.verbose) + { + string objPath = obj.name; + var prt = obj.transform.parent; + while(prt != null) + { + objPath = prt.name + "\\" + objPath; + prt = prt.parent; + } + ftRenderLightmap.simpleProgressBarEnd(); + if (!EditorUtility.DisplayDialog("Incorrect UVs", "Object " + objPath + " UVs are out of 0-1 bounds", "Continue", "Stop")) + { + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + ProgressBarInit("Exporting scene - preparing...", window); + } + + int overlap = ftModelPostProcessor.DoOverlapCheck(obj, false); + if (overlap != 0 && ftRenderLightmap.verbose) + { + //storage.debugRT = ftModelPostProcessor.rt; + string objPath = obj.name; + var prt = obj.transform.parent; + while(prt != null) + { + objPath = prt.name + "\\" + objPath; + prt = prt.parent; + } + if (overlap < 0) + { + ftRenderLightmap.simpleProgressBarEnd(); + if (!EditorUtility.DisplayDialog("Incorrect UVs", "Object " + objPath + " has no UV2", "Continue", "Stop")) + { + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + ProgressBarInit("Exporting scene - preparing...", window); + } + else + { + ftRenderLightmap.simpleProgressBarEnd(); + if (!EditorUtility.DisplayDialog("Incorrect UVs", "Object " + objPath + " has overlapping UVs", "Continue", "Stop")) + { + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + ProgressBarInit("Exporting scene - preparing...", window); + } + } + } + } + ftModelPostProcessor.EndOverlapCheck(); + } + + // Prepare progressbar + int progressNumObjects = 0; + foreach(GameObject obj in objects) + { + if (obj == null) continue; + if (!obj.activeInHierarchy) continue; + progressNumObjects++; + } + + // Open files to write + fscene = new BinaryWriter(File.Open(scenePath + "/objects.bin", FileMode.Create)); + fmesh = new BinaryWriter(File.Open(scenePath + "/mesh.bin", FileMode.Create)); + flmid = new BinaryWriter(File.Open(scenePath + "/lmid.bin", FileMode.Create)); + fseamfix = new BinaryWriter(File.Open(scenePath + "/seamfix.bin", FileMode.Create)); + fsurf = new BinaryWriter(File.Open(scenePath + "/surf.bin", FileMode.Create)); + fmatid = new BinaryWriter(File.Open(scenePath + "/matid.bin", FileMode.Create)); + fmatide = new BinaryWriter(File.Open(scenePath + "/emissiveid.bin", FileMode.Create)); + fmatideb = new BinaryWriter(File.Open(scenePath + "/emissivemul.bin", FileMode.Create)); + fmatidh = new BinaryWriter(File.Open(scenePath + "/heightmapid.bin", FileMode.Create)); + falphaid = new BinaryWriter(File.Open(scenePath + "/alphaid.bin", FileMode.Create)); + + fvbfull = new BufferedBinaryWriterFloat( new BinaryWriter(File.Open(scenePath + "/vbfull.bin", FileMode.Create)) ); + fvbtrace = new BufferedBinaryWriterFloat( new BinaryWriter(File.Open(scenePath + "/vbtrace.bin", FileMode.Create)) ); + fvbtraceTex = new BufferedBinaryWriterFloat( new BinaryWriter(File.Open(scenePath + "/vbtraceTex.bin", FileMode.Create)) ); + fvbtraceUV0 = new BufferedBinaryWriterFloat( new BinaryWriter(File.Open(scenePath + "/vbtraceUV0.bin", FileMode.Create)) ); + + fib = new BufferedBinaryWriterInt( new BinaryWriter(File.Open(scenePath + "/ib.bin", FileMode.Create)) ); + + fib32 = new BinaryWriter(File.Open(scenePath + "/ib32.bin", FileMode.Create)); + fib32lod = new BinaryWriter[sceneLodsUsed]; + for(int i=0; i 0) + { + //terrainObjectToHeightMapPtr = new IntPtr[terrainObjectToHeightMap.Count]; + /*for(int i=0; i(); + + for(int i=0; i= 0) { + for(int k=0; k 1) alphaRef = 1; + + if (alphaMetaPass) + { + // Will use meta pass alpha + + lmAlphaList.Add((System.IntPtr)0); // will be replaced after in-engine UVGBuffer part generation + if (nonDX11) lmAlphaListRAM.Add(new TexInput()); + lmAlphaListTex.Add(null); + lmAlphaRefList.Add(alphaRef); + lmAlphaChannelList.Add(id); // channel is always 3; store LMID instead + + texID = alphaCounter; + alphaCounter++; + alphaID = (ushort)texID; + + hasMetaAlpha = true; + } + else + { + // Using alpha texture directly + + // allow same map instances with different threshold + List texIDs; + if (!alphaMap.TryGetValue(texPtr, out texIDs)) + { + alphaMap[texPtr] = texIDs = new List(); + + lmAlphaList.Add(texPtr); + if (nonDX11) lmAlphaListRAM.Add(InputDataFromTex(tex)); + lmAlphaListTex.Add(tex); + lmAlphaRefList.Add(alphaRef); + lmAlphaChannelList.Add(alphaChannel); + + texIDs.Add(alphaCounter); + texID = alphaCounter; + alphaCounter++; + //Debug.Log("Alpha " + texID+": " + tex.name+" "+alphaRef); + alphaID = (ushort)texID; + } + else + { + int matchingInstance = -1; + for(int instance=0; instance()); + lmLocalToGlobalIndices.Add(new List()); + lmVOffset.Add(0); + } + + var mmr = GetValidRenderer(obj); + var castsShadows = mmr.shadowCastingMode != UnityEngine.Rendering.ShadowCastingMode.Off; + if (exportTerrainAsHeightmap && obj.name == "__ExportTerrain") castsShadows = false; // prevent exporting placeholder quads to ftrace + + time = GetTime(); + for(int k=0;k k) + { + if (mats[k] != null) + { + var matTag = mats[k].GetTag("RenderType", true); + if (matTag == "Transparent" || matTag == "TreeLeaf") + { + if (mats[k].HasProperty("_Color")) + { + if (mats[k].color.a < 0.5f) submeshCastsShadows = false; + } + } + } + } + } + + // Generate tracing index buffer, write alpha IDs per triangle + if (submeshCastsShadows) + { + var alphaID = alphaIDs[(alphaIDs.Count - m.subMeshCount) + k]; + + if (lodLevel < 0) + { + // Export persistent IB + var indicesOpaqueArray = indicesOpaque; + var indicesTransparentArray = indicesTransparent; + var falphaidFile = falphaid; + exportIB32(indicesOpaqueArray, indicesTransparentArray, id>=0 ? lmIndexArrays[id] : null, + inds[k], isFlipped, currentVoffset, id>=0 ? lmVOffset[id] : 0, falphaidFile, alphaID); + } + else + { + // Export LOD IBs + var visList = objToLodLevelVisible[obj]; + for(int vlod=0; vlod=0 ? lmIndexArrays[id] : null, + inds[k], isFlipped, currentVoffset, id>=0 ? lmVOffset[id] : 0, falphaidFile, alphaID); + } + } + } + ioffset += indexCount; + } + ibTime += GetTime() - time; + + if (id >= 0) + { + var vcount = objsToWriteVerticesPosW[i].Length;//m.vertexCount; + var remapArray = lmLocalToGlobalIndices[id]; + var addition = lmVOffset[id]; + for(int k=0; k(); + if (areaLight == null) + { + var areaIndex = temporaryAreaLightMeshList.IndexOf(obj); + if (areaIndex >= 0) areaLight = temporaryAreaLightMeshList2[areaIndex]; + } + //var areaLight = + if (areaLight != null) id = areaLight.lmid; + + var vertexBake = lmgroup != null ? (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) : false; + //var castsShadows = obj.GetComponent().shadowCastingMode != UnityEngine.Rendering.ShadowCastingMode.Off; + + var holderObj = objsToWriteHolder[i]; + if (holderObj != null) + { + if (!holderRect.TryGetValue(holderObj, out rc)) + { + holderObj = null; + } + } + + time = GetTime(); + //var vertices = m.vertices; + //var normals = m.normals; + //var tangents = m.tangents; + var uv = objsToWriteVerticesUV[i];//m.uv; + var uv2 = objsToWriteVerticesUV2[i];//m.uv2; + if (uv2.Length == 0 && !vertexBake) uv2 = uv;//m.uv; + vbTimeRead += GetTime() - time; + + var inds = objsToWriteIndices[i]; + + var time2 = GetTime(); + time = time2; + + // Transform UVs + var tformedPos = objsToWriteVerticesPosW[i];// new Vector3[vertices.Length]; + var tformedNormals = objsToWriteVerticesNormalW[i];// new Vector3[normals.Length]; + Vector4[] tformedTangents = null; + if (NeedsTangents(lmgroup, tangentSHLights)) + { + tformedTangents = objsToWriteVerticesTangentW[i]; + } + Vector2[] tformedUV2; + if (areaLight == null && !vertexBake) + { + tformedUV2 = holderObj == null ? uv2 : new Vector2[tformedPos.Length]; + for(int t=0; t= 0) + { + while(lmUVArrays.Count <= id) + { + lmUVArrays.Add(new List()); + } + var lmUVArray = lmUVArrays[id]; + for(int k=0; k lmgroup.resolution) + { + DebugLogWarning("Not generating LOD UVs for " + lmgroup.name + ", because there are too many UV islands"); + uvrUnload(); + continue; + } + DebugLogInfo("Min LOD resolution for " + lmgroup.name + " is " + minLodResolution); + for(int s=0; s= 0 && (int)u > id*10) + { + Debug.LogError("Float overflow (GI LOD)"); + } + lmUVArrays2[i][k * 2] = u; + } + } + } + + // Write vbTraceTex + int numTraceVerts = vbtraceTexUVArray.Count/2; + for(int i=0; i groupList[g].id && str.hasEmissive[groupList[g].id]; + + bool vertexBake = groupList[g].mode == BakeryLightmapGroup.ftLMGroupMode.Vertex; + + int res = groupList[g].resolution; + if (vertexBake) + { + if (groupList[g].totalVertexCount == 0) + { + DebugLogError("Vertex lightmap group " + groupList[g].name + " has 0 static vertices. Make sure objects inside the group don't all have Scale In Lightmap == 0."); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)groupList[g].totalVertexCount)); + atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)ftRenderLightmap.tileSize) * ftRenderLightmap.tileSize; + res = atlasTexSize; + } + + var bakeWithNormalMaps = (groupList[g].renderDirMode == BakeryLightmapGroup.RenderDirMode.BakedNormalMaps) ? + true : (ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.BakedNormalMaps); + + if (groupList[g].probes) bakeWithNormalMaps = false; + + bool hasMetaAlpha = false; + + ftUVGBufferGen.StartUVGBuffer(res, hasEmissive, bakeWithNormalMaps); + for(int i=0; i()) continue; + var bakedMesh = GetSharedMeshBaked(obj); + ftUVGBufferGen.RenderUVGBuffer(bakedMesh, + GetValidRenderer(obj), + objsToWriteScaleOffset[i], + obj.transform, + vertexBake, + objsToWriteUVOverride[i], + bakeWithNormalMaps && !exportTerrainAsHeightmap && obj.name == "__ExportTerrain", + objsToWriteHasMetaAlpha[i]); + + if (objsToWriteHasMetaAlpha[i]) hasMetaAlpha = true; + } + ftUVGBufferGen.EndUVGBuffer(); + + var albedo = ftUVGBufferGen.texAlbedo; + var emissive = ftUVGBufferGen.texEmissive; + var normal = ftUVGBufferGen.texNormal; + var alpha = ftUVGBufferGen.texAlpha; + if (hasEmissive) + { + //albedo = ftUVGBufferGen.GetAlbedoWithoutEmissive(ftUVGBufferGen.texAlbedo, ftUVGBufferGen.texEmissive); + //if ((unityVersionMajor == 2017 && unityVersionMinor < 2) || unityVersionMajor < 2017) + //{ +#if UNITY_2017_2_OR_NEWER +#else + // Unity before 2017.2: emissive packed to RGBM + // Unity after 2017.2: linear emissive + emissive = ftUVGBufferGen.DecodeFromRGBM(emissive); +#endif + //} + if (ftRenderLightmap.hackEmissiveBoost != 1.0f) + { + ftUVGBufferGen.Multiply(emissive, ftRenderLightmap.hackEmissiveBoost); + } + if (!vertexBake) ftUVGBufferGen.Dilate(emissive); + } + if (!vertexBake) ftUVGBufferGen.Dilate(albedo); + + if (isDX11) + { + SaveGBufferMap(albedo.GetNativeTexturePtr(), + scenePath + "/uvalbedo_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), + ftRenderLightmap.compressedGBuffer); + GL.IssuePluginEvent(5); + yield return null; + } + else + { + var bytes = InputBytesFromTex(albedo); + SaveGBufferMapFromRAM(bytes, bytes.Length, + scenePath + "/uvalbedo_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), + ftRenderLightmap.compressedGBuffer); + } + DestroyImmediate(albedo); + //if (g==2) storage.debugTex = emissive; + + if (hasEmissive) + { + if (isDX11) + { + SaveGBufferMap(emissive.GetNativeTexturePtr(), + scenePath + "/uvemissive_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), + ftRenderLightmap.compressedGBuffer); + GL.IssuePluginEvent(5); + yield return null; + } + else + { + var bytes = InputBytesFromTex(emissive, TexInputType.HalfColor); + SaveGBufferMapFromRAM(bytes, bytes.Length, + scenePath + "/uvemissive_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), + ftRenderLightmap.compressedGBuffer); + } + + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add("uvemissive_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds")); + DestroyImmediate(emissive); + } + + if (bakeWithNormalMaps) + { + if (isDX11) + { + SaveGBufferMap(normal.GetNativeTexturePtr(), + scenePath + "/uvnormal_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), + ftRenderLightmap.compressedGBuffer); + GL.IssuePluginEvent(5); + yield return null; + } + else + { + var bytes = InputBytesFromTex(normal); + SaveGBufferMapFromRAM(bytes, bytes.Length, + scenePath + "/uvnormal_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), + ftRenderLightmap.compressedGBuffer); + } + DestroyImmediate(normal); + } + + if (hasMetaAlpha) + { + for(int i=0; i 0) + { + if (isDX11) + { + var terrainObjectToHeightMapPtr = new IntPtr[terrainObjectToHeightMap.Count]; + for(int i=0; i tex2hash; + static Dictionary lightSaved; + static bool allowOverwrite = false; + + static System.Type texUtil; + static MethodInfo texUtil_GetUsage; + + static public void InitMaps(bool overwrite) + { + allowOverwrite = overwrite; + tex2hash = new Dictionary(); + lightSaved = new Dictionary(); + } + + static public void BuildDirectLight(BakeryDirectLight obj, int SAMPLES, bool ignoreNormal = false, string outName = "direct.bin") + { + if (!allowOverwrite && lightSaved.ContainsKey(outName)) return; + lightSaved[outName] = true; + + var folder = ftBuildGraphics.scenePath;//Directory.GetParent(Application.dataPath).FullName + "/frender"; + if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); + var f = new BinaryWriter(File.Open(folder + "/" + outName, FileMode.Create)); + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(outName); + + f.Write(obj.transform.forward.x); + f.Write(obj.transform.forward.y); + f.Write(obj.transform.forward.z); + + #if SRGBCONVERT + f.Write(obj.color.linear.r * obj.intensity); + f.Write(obj.color.linear.g * obj.intensity); + f.Write(obj.color.linear.b * obj.intensity); + #else + f.Write(obj.color.r * obj.intensity); + f.Write(obj.color.g * obj.intensity); + f.Write(obj.color.b * obj.intensity); + #endif + + f.Write(obj.shadowSpread); + f.Write(SAMPLES); + + f.Write(obj.cloudShadowTilingX); + f.Write(obj.cloudShadowTilingY); + f.Write(obj.cloudShadowOffsetX); + f.Write(obj.cloudShadowOffsetY); + + f.Write(ignoreNormal); + f.Write((short)0); + + if (obj.cloudShadow != null) + { + var tex = obj.cloudShadow; + int existingTexHash; + string texName = ""; + if (!tex2hash.TryGetValue(tex, out existingTexHash)) existingTexHash = -1; + if (existingTexHash < 0) + { + int texHash = tex.GetHashCode(); + tex2hash[tex] = texHash; + existingTexHash = texHash; + } + texName = "cookie_" + existingTexHash + ".dds"; + + // Save original texture to RGBA32F DDS + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11) + { + ftBuildGraphics.InitShaders(); + ftBuildGraphics.SaveCookie((tex as Texture2D).GetNativeTexturePtr(), + folder + "/" + texName + ); + GL.IssuePluginEvent(4); + } + else + { + var a = ftBuildGraphics.InputDataFromTex(tex, ftBuildGraphics.TexInputType.FloatColor); + ftBuildGraphics.SaveCookieFromRAM(a, + folder + "/" + texName + ); + } + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName); + + f.Write(texName); + } + else + { + f.Write((byte)0); + } + + f.Close(); + } + + static public void BuildSkyLight(BakerySkyLight obj, int SAMPLES, bool texDirty, string outName = "sky.bin") + { + if (!allowOverwrite && lightSaved.ContainsKey(outName)) return; + lightSaved[outName] = true; + + var folder = ftBuildGraphics.scenePath;//Directory.GetParent(Application.dataPath).FullName + "/frender"; + if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); + var f = new BinaryWriter(File.Open(folder + "/" + outName, FileMode.Create)); + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(outName); + + #if SRGBCONVERT + f.Write(obj.color.linear.r * obj.intensity); + f.Write(obj.color.linear.g * obj.intensity); + f.Write(obj.color.linear.b * obj.intensity); + #else + f.Write(obj.color.r * obj.intensity); + f.Write(obj.color.g * obj.intensity); + f.Write(obj.color.b * obj.intensity); + #endif + + f.Write(SAMPLES); + f.Write(obj.hemispherical); + + f.Write("sky" + obj.UID + ".dds"); + + f.Close(); + + if (texDirty) + { + /* + // Disable cubemap compression, so texture is half-float + var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(obj.cubemap)) as TextureImporter; + if (importer.textureCompression != TextureImporterCompression.Uncompressed) + { + importer.textureCompression = TextureImporterCompression.Uncompressed; + importer.SaveAndReimport(); + } + */ + + var tform = obj.transform; + var rh = tform.right; + var up = tform.up; + var fw = tform.forward; + + var texName = "sky" + obj.UID + ".dds"; + + bool isDoubleLDR = false; + + // Find out texture encoding + // Even if pixel format is the same, different encoding rules (dLDR, RGBM) can be used + if (texUtil == null) texUtil = Assembly.Load("UnityEditor.dll").GetType("UnityEditor.TextureUtil"); + if (texUtil == null) + { + Debug.LogError("TextureUtil class cannot be found"); + } + else + { + if (texUtil_GetUsage == null) texUtil_GetUsage = texUtil.GetMethod("GetUsageMode", BindingFlags.Static | BindingFlags.Public); + if (texUtil_GetUsage == null) + { + Debug.LogError("TextureUtil::GetUsage cannot be found"); + } + else + { + int usage = (int)texUtil_GetUsage.Invoke(null, new object[]{obj.cubemap}); + isDoubleLDR = usage == 1 // BakedLightmapDoubleLDR + || usage == 7;// DoubleLDR + } + } + + bool isLinear = PlayerSettings.colorSpace == ColorSpace.Linear; + + if (obj.correctRotation) + { + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11) + { + ftBuildGraphics.InitShaders(); + ftBuildGraphics.SaveSky(obj.cubemap.GetNativeTexturePtr(), + rh.x, + up.x, + fw.x, + rh.y, + up.y, + fw.y, + rh.z, + up.z, + fw.z, + folder + "/" + texName, + isLinear, + isDoubleLDR + ); + GL.IssuePluginEvent(3); // convert cubemap to small lat/lon DDS + } + else + { + var a = ftBuildGraphics.InputDataFromCubemap(obj.cubemap as Texture, Matrix4x4.TRS(Vector3.zero, obj.transform.rotation, Vector3.one).transpose, isLinear, isDoubleLDR, ftBuildGraphics.TexInputType.FloatColor); + ftBuildGraphics.SaveCookieFromRAM(a, + folder + "/" + texName + ); + } + } + else + { + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11) + { + ftBuildGraphics.InitShaders(); + ftBuildGraphics.SaveSky(obj.cubemap.GetNativeTexturePtr(), + obj.transform.right.x, + obj.transform.right.y, + obj.transform.right.z, + obj.transform.up.x, + obj.transform.up.y, + obj.transform.up.z, + obj.transform.forward.x, + obj.transform.forward.y, + obj.transform.forward.z, + folder + "/" + texName, + isLinear, + isDoubleLDR + ); + GL.IssuePluginEvent(3); // convert cubemap to small lat/lon DDS + } + else + { + var a = ftBuildGraphics.InputDataFromCubemap(obj.cubemap as Texture, Matrix4x4.TRS(Vector3.zero, obj.transform.rotation, Vector3.one), isLinear, isDoubleLDR, ftBuildGraphics.TexInputType.FloatColor); + ftBuildGraphics.SaveCookieFromRAM(a, + folder + "/" + texName + ); + } + } + + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName); + } + } + +#if OPTIMIZEDAREA + + class BSPNode + { + public BSPNode left, right; + public float probabilityDivide = 0.0f; + public List leafIndices; + }; + + static int GetRandomTriFromBSP(BSPNode bspNode, float rndValue) + { + //Debug.LogError(bspNode.probabilityDivide); + if (bspNode.leafIndices != null) + { + return bspNode.leafIndices[Random.Range(0, bspNode.leafIndices.Count)]; + } + if (rndValue < bspNode.probabilityDivide) + { + return GetRandomTriFromBSP(bspNode.left, rndValue);// != null ? bspNode.left : bspNode.right, rndValue); + } + else + { + return GetRandomTriFromBSP(bspNode.right, rndValue);// != null ? bspNode.right : bspNode.left, rndValue); + } + } + + static BSPNode BuildProbabilityBSP(int[] triIndices, float[] area, int start, int end, int depth, float parentGlobalOffset, float parentGlobalEnd) + { + if (depth > 100) return null; + + var bspNode = new BSPNode(); + + int startIndex = triIndices[start]; + int endIndex = triIndices[end]; + + // Decide where to split + //float probabilityDivide = (area[startIndex] + area[endIndex]) * 0.5f;/// (end - start);// * 0.5f; + float probabilitySum = 0; + int divisor = start; + for(int i=start; i<=end; i++) + { + int triIndex = triIndices[i]; + probabilitySum += area[triIndex]; + //if (probabilitySum >= probabilityDivide) + //if (area[triIndex] >= probabilityDivide) + { + //divisor = i; + //break; + } + } + float probabilityDivide = probabilitySum / (end - start); + + //probabilitySum = 0; + for(int i=start; i<=end; i++) + { + int triIndex = triIndices[i]; + //probabilitySum += area[triIndex]; + //if (probabilitySum >= probabilityDivide) + if (area[triIndex] >= probabilityDivide) + { + divisor = i - 1; + break; + } + } + + if (divisor < 0) divisor = 0; + + int beforeDivisorIndex = divisor > 0 ? triIndices[divisor-1] : 0; + int divisorIndex = triIndices[divisor]; + + //Debug.LogError(start+" "+end+" "+divisor+" "+probabilityDivide); + + /* + // Create new BSP depth layer, if needed + if (layers.Count <= depth) + { + int numElements = triIndices.Length; // conservative? + layers[depth] = new int[numElements]; + } + */ + + //bspNode.probabilityDivide = probabilityDivide; + float probabilitySumLeft = 0; + float probabilitySumRight = 0; + for(int i=start; i<=end; i++) + { + int triIndex = triIndices[i]; + if (i <= divisor) + { + probabilitySumLeft += area[triIndex]; + } + else + { + probabilitySumRight += area[triIndex]; + } + } + //probabilitySumLeft /= divisor - start + 1; + //probabilitySumRight /= end - divisor; + float probabilityLength = probabilitySumLeft + probabilitySumRight; + //bspNode.probabilityDivide = parentGlobalOffset + (probabilitySumLeft / probabilityLength) * parentGlobalPercent; + bspNode.probabilityDivide = Mathf.Lerp(parentGlobalOffset, parentGlobalEnd, probabilitySumLeft / probabilityLength); + + //bspNode.leafIndex = startIndex; + bool isLeaf = true; + + if (divisor != start && divisor != end) + { + //Debug.LogError("["+depth+"] Divide global " + bspNode.probabilityDivide+" "+start+" "+divisor+" "+end+" "+probabilitySumLeft + " "+probabilitySumRight+" "+parentGlobalOffset+" "+parentGlobalEnd); + + // Split left part + int newStart = start; + int newEnd = divisor > 0 ? divisor : 0; + //Debug.LogError("left"); + bspNode.left = BuildProbabilityBSP(triIndices, area, newStart, newEnd, depth + 1, parentGlobalOffset, bspNode.probabilityDivide); + + // Split right part + newStart = divisor + 1; + newEnd = end; + //Debug.LogError("right"); + bspNode.right = BuildProbabilityBSP(triIndices, area, newStart, newEnd, depth + 1, bspNode.probabilityDivide, parentGlobalEnd); + + isLeaf = false; + } + + if (isLeaf) + { + bspNode.leafIndices = new List(); + string l = ""; + for(int i=start; i<=end; i++) + { + int triIndex = triIndices[i]; + bspNode.leafIndices.Add(triIndex); + l += area[triIndex] + ", "; + } + //Debug.LogError("Leaf: " + l); + } + + return bspNode; + } +#endif + + static public float BuildLight(BakeryLightMesh obj, int SAMPLES, Vector3[] corners, string outName = "lights.bin", + List vplData = null) + { + if (!allowOverwrite && lightSaved.ContainsKey(outName)) return 0.0f; + lightSaved[outName] = true; + + var folder = ftBuildGraphics.scenePath;//Directory.GetParent(Application.dataPath).FullName + "/frender"; + if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); + var f = new BinaryWriter(File.Open(folder + "/" + outName, FileMode.Create)); + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(outName); + + f.Write(1); + + Mesh mesh = null; + var tform = obj.transform; + Vector3[] verts; + Vector2[] uv = null; + int[] indices; + int tris; + if (corners == null) + { + mesh = ftBuildGraphics.GetSharedMeshBaked(obj.gameObject); + verts = mesh.vertices; + indices = mesh.triangles; + tris = indices.Length / 3; + if (obj.texture != null) uv = mesh.uv; + } + else + { + verts = corners; + indices = new int[6]; + indices[0] = 2; + indices[1] = 1; + indices[2] = 0; + indices[3] = 0; + indices[4] = 3; + indices[5] = 2; + tris = 2; + if (obj.texture != null) + { + uv = new Vector2[4]; + uv[0] = new Vector2(0,0); + uv[1] = new Vector2(0,1); + uv[2] = new Vector2(1,1); + uv[3] = new Vector2(1,0); + } + } + + float[] area = new float[tris]; +#if (OPTIMIZEDAREA || OPTIMIZEDAREA2) +#else + float minArea = float.MaxValue; + float maxArea = -float.MaxValue; +#endif + float totalWorldArea = 0; + + //Vector2[] uv = null; + int downsampleRes = 0; + float[] pixels = null; + string texName = ""; + if (obj.texture != null) + { + //uv = mesh.uv; + var tex = obj.texture; + + // Save original texture to RGBA32F DDS + int existingTexHash; + if (!tex2hash.TryGetValue(tex, out existingTexHash)) existingTexHash = -1; + if (existingTexHash < 0) + { + int texHash = tex.GetHashCode(); + tex2hash[tex] = texHash; + existingTexHash = texHash; + } + texName = "areatex_" + existingTexHash + ".dds"; + + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11) + { + ftBuildGraphics.InitShaders(); + ftBuildGraphics.SaveCookie(tex.GetNativeTexturePtr(), + folder + "/" + texName + ); + GL.IssuePluginEvent(4); + } + else + { + var a = ftBuildGraphics.InputDataFromTex(tex, ftBuildGraphics.TexInputType.FloatColor); + ftBuildGraphics.SaveCookieFromRAM(a, + folder + "/" + texName + ); + } + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName); + + // Get downsampled (via mips) texture + downsampleRes = (int)Mathf.Sqrt(SAMPLES); + if (downsampleRes == 0) downsampleRes = 1; + var downsampleRT = new RenderTexture(downsampleRes, downsampleRes, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear); + var downsampleTex = new Texture2D(downsampleRes, downsampleRes, TextureFormat.RGBAFloat, false, true); + Graphics.Blit(tex, downsampleRT); + Graphics.SetRenderTarget(downsampleRT); + downsampleTex.ReadPixels(new Rect(0,0,downsampleRes,downsampleRes), 0, 0, false); + downsampleTex.Apply(); + var bytes = downsampleTex.GetRawTextureData(); + Object.DestroyImmediate(downsampleTex); + downsampleRT.Release(); + pixels = new float[bytes.Length / 4]; + System.Buffer.BlockCopy(bytes, 0, pixels, 0, bytes.Length); + } + + for(int j=0; j 0) totalWorldArea += area[j]; +#else + area[j] = Vector3.Cross(v2 - v1, v3 - v1).magnitude; + if (area[j] > 0) totalWorldArea += area[j]; + if (area[j] > 0) + { + minArea = Mathf.Min(minArea, area[j]); + maxArea = Mathf.Max(maxArea, area[j]); + } +#endif + } + +#if OPTIMIZEDAREA2 + + // New 2 + var randomTriIndices = new int[SAMPLES]; + // When an area light is with a width or height of 0, this will avoid an OOR exception (this will keep the baking running instead of crashing it...) + float invTotalArea = totalWorldArea == 0 ? 0 : (1.0f / (totalWorldArea * 0.5f)); + float sumWeights = 0.0f; + for(int j=0; j= weightSoFar && outputSampleIx + 1 < tris) + { + weightSoFar += area[++outputSampleIx]; + } + randomTriIndices[i] = outputSampleIx; + } + +#elif OPTIMIZEDAREA + + // New + + // Collect indices to triangles + var triIndices = new int[tris]; + float invTotalArea = 1.0f / (totalWorldArea * 0.5f); + for(int j=0; j Larger + System.Array.Sort(triIndices, delegate(int a, int b) + { + return area[a].CompareTo(area[b]); + }); + + // Put triangle indices into a BSP tree based on area + int start = 0; + int end = triIndices.Length - 1; + //var bspLayers = new List(); // tri index array per depth level + var bspRoot = BuildProbabilityBSP(triIndices, area, start, end, 0, 0.0f, 1.0f); + +#else + // Legacy + if (maxArea / minArea > 65535) + { + minArea = maxArea / 65535; + } + float invMinArea = 1.0f / minArea; + for(int j=0; j(); + for(int j=0; j 0 && tarea < 65536) + { + for(int k=0; k 0) Debug.LogError("Skipped " + skipped + " invalid triangles out of " + tris + " on LightMesh " + obj.name + " (area is too big?)"); +#endif + + + f.Write(obj.samples2); + f.Write(SAMPLES); + Vector3 trinormal; + for(int sample=0; sample 0 ? uniformTriList[rndTri] : 0; +#endif + + var rndA = Random.value; + var rndB = Random.value; + var rndC = Random.value; + + var A = verts[indices[tri*3]]; + var B = verts[indices[tri*3+1]]; + var C = verts[indices[tri*3+2]]; + var point = (1.0f - Mathf.Sqrt(rndA)) * A + (Mathf.Sqrt(rndA) * (1.0f - rndB)) * B + (Mathf.Sqrt(rndA) * rndB) * C; + + if (corners == null) point = tform.TransformPoint(point); + + trinormal = Vector3.Cross(A - B, B - C);//.normalized; + float len = Mathf.Sqrt(trinormal.x*trinormal.x + trinormal.y*trinormal.y + trinormal.z*trinormal.z); + trinormal /= len; + + if (corners == null) trinormal = tform.TransformDirection(trinormal); + + point += trinormal * 0.001f; + + f.Write(point.x); + f.Write(point.y); + f.Write(point.z); + if (vplData != null) vplData.Add(point); + + f.Write(trinormal.x); + f.Write(trinormal.y); + f.Write(trinormal.z); + if (vplData != null) vplData.Add(trinormal); + + if (obj.texture != null) + { + var tA = uv[indices[tri*3]]; + var tB = uv[indices[tri*3+1]]; + var tC = uv[indices[tri*3+2]]; + var tpoint = (1.0f - Mathf.Sqrt(rndA)) * tA + (Mathf.Sqrt(rndA) * (1.0f - rndB)) * tB + (Mathf.Sqrt(rndA) * rndB) * tC; + int tx = (int)((tpoint.x - Mathf.Floor(tpoint.x)) * (downsampleRes - 1)); + int ty = (int)((tpoint.y - Mathf.Floor(tpoint.y)) * (downsampleRes - 1)); + int pixelIndex = ty * downsampleRes + tx; + if (pixelIndex*4+2 < pixels.Length) + { + float cr = pixels[pixelIndex * 4]; + float cg = pixels[pixelIndex * 4 + 1]; + float cb = pixels[pixelIndex * 4 + 2]; + f.Write(cr); + f.Write(cg); + f.Write(cb); + if (vplData != null) vplData.Add(new Vector3(cr, cg, cb)); + } + else + { + f.Write(0.0f); + f.Write(0.0f); + f.Write(0.0f); + if (vplData != null) vplData.Add(Vector3.one); + } + } + else if (vplData != null) + { + vplData.Add(Vector3.one); + } + + //var g = GameObject.CreatePrimitive(PrimitiveType.Sphere); + //g.transform.position = point; + //g.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f); + } + + f.Write(obj.cutoff); + f.Write(totalWorldArea * 0.5f); + + #if SRGBCONVERT + f.Write(obj.color.linear.r * obj.intensity); + f.Write(obj.color.linear.g * obj.intensity); + f.Write(obj.color.linear.b * obj.intensity); + #else + f.Write(obj.color.r * obj.intensity); + f.Write(obj.color.g * obj.intensity); + f.Write(obj.color.b * obj.intensity); + #endif + + f.Write(obj.lmid); + + if (obj.texture != null) + { + f.Write(texName); + } + + f.Close(); + + return totalWorldArea * 0.5f; + } + + static public string GetTempTexName(Object tex, string prefix = "cookie_") + { + int existingTexHash; + if (!tex2hash.TryGetValue(tex, out existingTexHash)) existingTexHash = -1; + if (existingTexHash < 0) + { + int texHash = tex.GetHashCode(); + tex2hash[tex] = texHash; + existingTexHash = texHash; + } + return prefix + existingTexHash + ".dds"; + } + + static public bool BuildLight(BakeryPointLight obj, int SAMPLES, bool texDirty, bool ignoreNormal = false, string outName = "pointlight.bin") + { + if (!allowOverwrite && lightSaved.ContainsKey(outName)) return false; + lightSaved[outName] = true; + + bool isError = false; + + var folder = ftBuildGraphics.scenePath;//Directory.GetParent(Application.dataPath).FullName + "/frender"; + if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); + var f = new BinaryWriter(File.Open(folder + "/" + outName, FileMode.Create)); + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(outName); + + f.Write(SAMPLES); + f.Write(obj.cutoff); + + float fakeDistMult = 1.0f; + float falloffMinRadius = obj.falloffMinRadius; + if (!obj.realisticFalloff) + { + fakeDistMult = (1.0f / obj.cutoff) * 5.0f; + falloffMinRadius = 1; + } + f.Write(fakeDistMult); + f.Write(falloffMinRadius); + + #if SRGBCONVERT + f.Write(obj.color.linear.r * obj.intensity); + f.Write(obj.color.linear.g * obj.intensity); + f.Write(obj.color.linear.b * obj.intensity); + #else + f.Write(obj.color.r * obj.intensity); + f.Write(obj.color.g * obj.intensity); + f.Write(obj.color.b * obj.intensity); + #endif + + var pos = obj.transform.position; + + f.Write(pos.x); + f.Write(pos.y); + f.Write(pos.z); + + f.Write(obj.shadowSpread); + f.Write(ignoreNormal); + + bool isCookie = obj.projMode == BakeryPointLight.ftLightProjectionMode.Cookie && obj.cookie != null; + bool isCone = obj.projMode == BakeryPointLight.ftLightProjectionMode.Cone; + bool isCubemap = obj.projMode == BakeryPointLight.ftLightProjectionMode.Cubemap && obj.cubemap != null; + bool isIES = obj.projMode == BakeryPointLight.ftLightProjectionMode.IES && obj.iesFile != null; + + int existingTexHash; + string texName = ""; + UnityEngine.Object tex = null; + if (isCookie || isCubemap || isIES) + { + if (isCookie) + { + tex = obj.cookie; + } + else if (isCubemap) + { + tex = obj.cubemap; + } + else + { + tex = obj.iesFile; + } + if (!tex2hash.TryGetValue(tex, out existingTexHash)) existingTexHash = -1; + if (existingTexHash < 0) + { + int texHash = tex.GetHashCode(); + tex2hash[tex] = texHash; + existingTexHash = texHash; + } + texName = "cookie_" + existingTexHash + ".dds"; + } + + if (isCone) + { + f.Write(obj.transform.forward.x); + f.Write(obj.transform.forward.y); + f.Write(obj.transform.forward.z); + f.Write(obj.angle); + f.Write(obj.innerAngle / 100.0f); + } + + if (isCookie || isCubemap || isIES) + { + if (isIES && obj.directionMode == BakeryPointLight.Direction.PositiveZ) + { + f.Write(obj.transform.right.x); + f.Write(obj.transform.right.y); + f.Write(obj.transform.right.z); + f.Write(-obj.transform.forward.x); + f.Write(-obj.transform.forward.y); + f.Write(-obj.transform.forward.z); + f.Write(obj.transform.up.x); + f.Write(obj.transform.up.y); + f.Write(obj.transform.up.z); + } + else + { + f.Write(obj.transform.right.x); + f.Write(obj.transform.right.y); + f.Write(obj.transform.right.z); + f.Write(obj.transform.up.x); + f.Write(obj.transform.up.y); + f.Write(obj.transform.up.z); + f.Write(obj.transform.forward.x); + f.Write(obj.transform.forward.y); + f.Write(obj.transform.forward.z); + } + f.Write(texName); + } + + if (isCookie) f.Write(obj.angle); + + if (texDirty) + { + if (!SavePointLightTexture(tex, folder, texName, isCookie, isCubemap, isIES)) isError = true; + } + + f.Close(); + + return isError; + } + + static void WriteNullTerminatedStringWithNewLine(BinaryWriter f, string s) + { + for(int i=0; i 0) + { + samples = System.Math.Max(samples / sampleDiv, 1); + } + flights.Write(samples); + } + + flights.Write(ignoreNormal ? (byte)1 : (byte)0); + + for(int L=start; L<=end; L++) + { + if (skipLight[L]) continue; + + var obj = allPoints[L]; + + bool isCookie = obj.projMode == BakeryPointLight.ftLightProjectionMode.Cookie && obj.cookie != null; + bool isCubemap = obj.projMode == BakeryPointLight.ftLightProjectionMode.Cubemap && obj.cubemap != null; + bool isIES = obj.projMode == BakeryPointLight.ftLightProjectionMode.IES && obj.iesFile != null; + + int existingTexHash; + string texName = ""; + UnityEngine.Object tex = null; + if (isCookie || isCubemap || isIES) + { + if (isCookie) + { + tex = obj.cookie; + } + else if (isCubemap) + { + tex = obj.cubemap; + } + else + { + tex = obj.iesFile; + } + if (!tex2hash.TryGetValue(tex, out existingTexHash)) existingTexHash = -1; + if (existingTexHash < 0) + { + int texHash = tex.GetHashCode(); + tex2hash[tex] = texHash; + existingTexHash = texHash; + } + texName = "cookie_" + existingTexHash + ".dds"; + WriteNullTerminatedStringWithNewLine(flights, texName); + } + + if (!SavePointLightTexture(tex, folder, texName, isCookie, isCubemap, isIES)) isError = true; + } + + flights.Close(); + + return isError; + } +} + +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildLights.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildLights.cs.meta new file mode 100644 index 00000000..5eae7d64 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildLights.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6d2a81f0f9d2d0c49bc8e08c6e18e72c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClearMenu.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClearMenu.cs new file mode 100644 index 00000000..5f55b921 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClearMenu.cs @@ -0,0 +1,153 @@ +using UnityEngine; +using UnityEditor; +using UnityEngine.SceneManagement; +using System.Collections; +using System.Collections.Generic; + +public class ftClearMenu : EditorWindow +{ + public enum SceneClearingMode + { + nothing = 0, + lightmapReferences = 1, + lightmapReferencesAndBakeSettings = 2 + } + + static public string[] options = new string[] {"Nothing", "Baked data references", "All (data and bake settings)"}; + + public SceneClearingMode sceneClearingMode = SceneClearingMode.lightmapReferences; + public bool clearLightmapFiles = false; + + [MenuItem("Bakery/Utilities/Clear baked data", false, 44)] + private static void ClearBakedDataShow() + { + var instance = (ftClearMenu)GetWindow(typeof(ftClearMenu)); + instance.titleContent.text = "Clear menu"; + instance.minSize = new Vector2(320, 90); + instance.maxSize = new Vector2(instance.minSize.x, instance.minSize.y + 1); + instance.Show(); + } + + void OnGUI() + { + sceneClearingMode = (SceneClearingMode)EditorGUILayout.Popup("Clear from scenes", (int)sceneClearingMode, options); + clearLightmapFiles = EditorGUILayout.Toggle("Delete lightmap files", clearLightmapFiles); + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + if (GUILayout.Button("Clear")) + { + string txt = ""; + if (sceneClearingMode == SceneClearingMode.nothing) + { + if (clearLightmapFiles) + { + txt += "Delete currently used lightmap files"; + } + else + { + return; + } + } + else + { + if (sceneClearingMode == SceneClearingMode.lightmapReferences) + { + txt = "Clear all Bakery data for currently loaded scenes"; + } + else + { + txt = "Clear all Bakery data and settings for currently loaded scenes"; + } + if (clearLightmapFiles) txt += " and delete currently used lightmap files"; + } + + if (EditorUtility.DisplayDialog("Bakery", txt + "?", "Yes", "No")) + { + ClearBakedData(sceneClearingMode, clearLightmapFiles); + } + } + } + + static void RemoveFiles(Texture2D map) + { + var path = AssetDatabase.GetAssetPath(map); + AssetDatabase.DeleteAsset(path); + ftRenderLightmap.DebugLogInfo("Deleted " + path); + } + + static void RemoveFiles(List maps) + { + for(int i=0; i(); + if (storage == null) continue; + + RemoveFiles(storage.maps); + RemoveFiles(storage.masks); + RemoveFiles(storage.dirMaps); + RemoveFiles(storage.rnmMaps0); + RemoveFiles(storage.rnmMaps1); + RemoveFiles(storage.rnmMaps2); + } + } + + if (sceneClearMode == SceneClearingMode.lightmapReferences) + { + var newStorages = new List(); + var sceneCount = SceneManager.sceneCount; + for(int i=0; i(); + if (storage != null) + { + var newGO = new GameObject(); + var newStorage = newGO.AddComponent(); + ftLightmapsStorage.CopySettings(storage, newStorage); + newStorages.Add(newGO); + } + Undo.DestroyObjectImmediate(go); + } + LightmapSettings.lightmaps = new LightmapData[0]; + for(int i=0; i app2serverTask = new Dictionary + { + {"ftrace", SERVERTASK_FTRACE}, + {"ftraceRTX", SERVERTASK_FTRACERTX}, + {"combineMasks", SERVERTASK_COMBINEMASKS}, + + {"denoiserLegacy", SERVERTASK_DENOISE5}, + {"denoiser", SERVERTASK_DENOISE6}, + {"denoiser72", SERVERTASK_DENOISE7}, + {"denoiserOIDN", SERVERTASK_DENOISEOIDN}, + + {"halffloat2hdr", SERVERTASK_HF2HDR}, + {"rgba2tga", SERVERTASK_RGBA2TGA}, + {"seamfixer", SERVERTASK_SEAMFIX}, + {"lmrebake (simple)", SERVERTASK_LMREBAKESIMPLE}, + {"lmrebake", SERVERTASK_LMREBAKE} + + }; + public static List serverFileList, serverGetFileList; + public static int serverGetFileIterator = 0; + + + public static IEnumerator UpdateConnection()//WaitForMessages() + { + var ipAdd = System.Net.IPAddress.Parse(serverAddress); + var remoteEP = new IPEndPoint(ipAdd, 27777); + var request = new byte[1]; + request[0] = SERVERTASK_REPORTSTATUS; + var requestGet = new byte[5]; + requestGet[0] = SERVERTASK_GETDATAREADY; + int numTasks = 1; + var taskGet = new byte[1]; + var nullByte = new byte[1]; + taskGet[0] = SERVERTASK_GETDATA; + nullByte[0] = 0; + + lastServerMsg = "Connecting..."; + lastServerErrorCode = 0; + lastServerMsgIsError = false; + var status = new byte[256]; + byte[] fileBuffer = null; + bool waitingForGet = false; + + while (connectedToServer) + { + if (statusSocket != null) + { + statusSocket.Close(); + statusSocket = null; + } + + waitingForGet = false; + + + // Attempt connecting to server + bool connectionInProgress = true; + bool connectionError = false; + double timeout = ftRenderLightmap.GetTimeMs() + serverConnectionTimeout; + while(connectionInProgress) + { + connectionInProgress = false; + try + { + if (statusSocket == null) + { + statusSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + statusSocket.Blocking = false; + statusSocket.Connect(remoteEP); + } + else + { + if (statusSocket.Poll(0, SelectMode.SelectError)) + { + connectionError = true; + break; + } + if (!statusSocket.Poll(0, SelectMode.SelectWrite) && ftRenderLightmap.GetTimeMs() < timeout) + { + connectionInProgress = true; + } + } + } + catch(SocketException s) + { + if (s.ErrorCode == 10035) // WSAEWOULDBLOCK + { + connectionInProgress = true; + } + else + { + connectionError = true; + break; + } + } + if (connectionInProgress) yield return null; + } + statusSocket.Blocking = true; + + // Send request(s) + try + { + if (connectionError) throw new SocketException(); + if (serverGetDataMode && serverGetFileList == null) serverGetDataMode = false; + if (serverGetDataMode && serverGetFileList.Count <= serverGetFileIterator) + { + serverMustRefreshData = true; + serverGetDataMode = false; + } + if (serverGetDataMode) + { + var fname = serverGetFileList[serverGetFileIterator]; + if (lastServerFile != fname) + { + int len = fname.Length; + statusSocket.Send(System.BitConverter.GetBytes(numTasks)); + statusSocket.Send(taskGet); + statusSocket.Send(System.BitConverter.GetBytes(len)); + statusSocket.Send(Encoding.ASCII.GetBytes(fname)); + statusSocket.Send(nullByte); + statusSocket.Close(); + + statusSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + statusSocket.Connect(remoteEP); + statusSocket.Send(request); +#if BAKERY_NETDEBUG + Debug.Log("Request sent (load file " + fname + ")"); +#endif + } + else + { + fileBuffer = new byte[lastServerFileSize]; + System.Buffer.BlockCopy(System.BitConverter.GetBytes(lastServerFileHash), 0, requestGet, 1, 4); + statusSocket.Send(requestGet); +#if BAKERY_NETDEBUG + Debug.Log("Request sent (get file)"); +#endif + waitingForGet = true; + } + } + else + { + statusSocket.Send(request); +#if BAKERY_NETDEBUG + Debug.Log("Request sent"); +#endif + } + } + catch(SocketException s) + { + lastServerMsg = "Failed to get data from server (" + s.ErrorCode + ")"; + lastServerMsgIsError = true; + lastServerErrorCode = 0; + + Debug.LogError(lastServerMsg); + statusSocket.Close(); + statusSocket = null; + statusProc = null; + //statusThread = null; + connectedToServer = false; + //return; + yield break; + } + +#if BAKERY_NETDEBUG + Debug.Log("Waiting for server to respond"); +#endif + + int serverErrCode = 0; + int appCode = 0; + int appErrCode = 0; + int textLen = 0; + int fileReady = 0; + int fileHash = 0; + int fileSize = 0; + string text = ""; + string fileNameReady = ""; + + int byteCount = 0; + bool interrupted = false; + double maxTimeToReceive = 10.0; + double timeToInterrupt = ftRenderLightmap.GetTimeMs() + maxTimeToReceive; + + while(!interrupted) + { + if (ftRenderLightmap.GetTimeMs() > timeToInterrupt) + { + timeToInterrupt = ftRenderLightmap.GetTimeMs() + maxTimeToReceive; + yield return null; + } + //while(statusSocket.Available == 0) yield return null; + //while(!statusSocket.Poll(0, SelectMode.SelectRead)) yield return null; + try + { + //while(true) + //{ + if (waitingForGet) + { + int bytesReceived = statusSocket.Receive(fileBuffer, byteCount, fileBuffer.Length - byteCount, SocketFlags.None); + byteCount += bytesReceived; + //Debug.Log("Received " + bytesReceived); + if (bytesReceived == 0) interrupted = true;//break; + } + else + { + byteCount = statusSocket.Receive(status); + //break; + interrupted = true; + } + //} + } + catch + { + if (waitingForGet) + { + Debug.LogError("Error getting file from server - retrying"); + lastServerFile = ""; + } + else + { + lastServerMsg = "Server disconnected"; + lastServerMsgIsError = true; + lastServerErrorCode = 0; + + Debug.LogError(lastServerMsg); + statusSocket.Close(); + statusSocket = null; + //statusThread = null; + statusProc = null; + connectedToServer = false; + yield break; + } + } + } + + if (byteCount > 0) + { + if (waitingForGet) + { + Debug.Log("Data received: " + byteCount); + var ext = lastServerFile.Substring(lastServerFile.Length-3).ToLower(); + string outPath; + if (ext == "lz4" || ext == "dds") + { + outPath = ftRenderLightmap.scenePath + "/" + lastServerFile; + } + else + { + outPath = "Assets/" + ftRenderLightmap.outputPath + "/" + lastServerFile; + } + BinaryWriter bw = null; + try + { + bw = new BinaryWriter(File.Open(outPath, FileMode.Create)); + } + catch + { + Debug.LogError("Failed writing " + outPath); + } + if (bw != null) + { + bw.Write(fileBuffer); + bw.Close(); + Debug.Log("File saved: " + outPath); + } + yield return null; + serverGetFileIterator++; + } + else + { + if (byteCount == 150) + { + serverErrCode = System.BitConverter.ToInt32(status, 0); + appCode = System.BitConverter.ToInt32(status, 4); + appErrCode = System.BitConverter.ToInt32(status, 8); + textLen = status[12]; + fileReady = status[13]; + fileHash = System.BitConverter.ToInt32(status, 14); + fileSize = System.BitConverter.ToInt32(status, 18); + if (textLen > 0) + { + text = Encoding.ASCII.GetString(status, 22, textLen); + } + if (fileReady > 0) + { + fileNameReady = Encoding.ASCII.GetString(status, 22 + textLen + 1, fileReady); + } + } + else + { + serverErrCode = SERVERERROR_UNKNOWN; + Debug.LogError("Unrecognized response size: " + byteCount); + } + //if (serverErrCode != 0) + { + var serverMsg = "Server: " + ftErrorCodes.TranslateServer(serverErrCode, appCode, appErrCode); + bool isError = serverErrCode != SERVERERROR_IDLE && serverErrCode != SERVERERROR_BUSY; + if (isError) + { + Debug.LogError(serverMsg); + } + else + { +#if BAKERY_NETDEBUG + Debug.Log(serverMsg); +#else + if (lastServerMsg != serverMsg) Debug.Log(serverMsg); +#endif + } + lastServerMsg = serverMsg; + lastServerMsgIsError = isError; + lastServerErrorCode = serverErrCode; + lastServerScene = text; + lastServerFile = fileNameReady; + lastServerFileHash = fileHash; + lastServerFileSize = fileSize; + } + } + } + + + if (!serverGetDataMode) + { + //var sleepTime = timeToUpdateServerStatus - curTime; + //if (sleepTime > 0) System.Threading.Thread.Sleep((int)sleepTime); + while(true) + { + var curTime = ftRenderLightmap.GetTimeMs(); + if (curTime >= timeToUpdateServerStatus) break; + yield return null; + } + + timeToUpdateServerStatus = ftRenderLightmap.GetTimeMs() + serverStatusInterval; + } + } + + statusSocket.Close(); + statusSocket = null; + //statusThread = null; + statusProc = null; + } + + public static void Disconnect() + { + if (statusSocket != null) + { + statusSocket.Close(); + statusSocket = null; + } + + statusProc = null; + /*if (statusThread != null) + { + statusThread.Abort(); + statusThread = null; + }*/ + + connectedToServer = false; + serverGetDataMode = false; + } + + public static void ConnectToServer() + { + try + { + Disconnect(); + connectedToServer = true; + + timeToUpdateServerStatus = 0; + //statusThread = new System.Threading.Thread(WaitForMessages); + //statusThread.Start(); + statusProc = UpdateConnection(); + statusProc.MoveNext(); + } + catch + { + Debug.LogError("Failed getting data from server"); + throw; + } + } + + public static bool SendRenderSequence(byte[] renderSequence) + { + Socket soc = null; + var ipAdd = System.Net.IPAddress.Parse(serverAddress); + var remoteEP = new IPEndPoint(ipAdd, serverPort); + + for(int i=0; i fileList) + { + serverGetFileList = fileList; + serverGetFileIterator = 0; + serverGetDataMode = true; + } + + public static void Update() + { + if (statusProc != null) statusProc.MoveNext(); + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClient.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClient.cs.meta new file mode 100644 index 00000000..4c8d6c30 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClient.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 500a77e47a646b24581261ad5e43fe3d +timeCreated: 1552557323 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftCreateMenu.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftCreateMenu.cs new file mode 100644 index 00000000..36378f95 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftCreateMenu.cs @@ -0,0 +1,96 @@ +using UnityEngine; +using UnityEditor; + +public class ftCreateMenu +{ + [MenuItem("Bakery/Create/Directional Light", false, 20)] + private static void CreateDirectionalLight() + { + var go = new GameObject(); + Undo.RegisterCreatedObjectUndo(go, "Create Bakery light"); + go.AddComponent(); + go.name = "DirectLight"; + var ecam = SceneView.lastActiveSceneView.camera.transform; + go.transform.position = ecam.position + ecam.forward; + go.transform.eulerAngles = new Vector3(50, -30, 0); + var arr = new GameObject[1]; + arr[0] = go; + Selection.objects = arr; + } + + [MenuItem("Bakery/Create/Skylight", false, 20)] + private static void CreateSkyLight() + { + var go = new GameObject(); + Undo.RegisterCreatedObjectUndo(go, "Create Bakery light"); + go.AddComponent(); + go.name = "Skylight"; + var ecam = SceneView.lastActiveSceneView.camera.transform; + go.transform.position = ecam.position + ecam.forward; + var arr = new GameObject[1]; + arr[0] = go; + Selection.objects = arr; + } + + [MenuItem("Bakery/Create/Point Light", false, 20)] + private static void CreatePointLight() + { + var go = new GameObject(); + Undo.RegisterCreatedObjectUndo(go, "Create Bakery light"); + go.AddComponent(); + go.name = "PointLight"; + var ecam = SceneView.lastActiveSceneView.camera.transform; + go.transform.position = ecam.position + ecam.forward; + var arr = new GameObject[1]; + arr[0] = go; + Selection.objects = arr; + } + + [MenuItem("Bakery/Create/Area Light (Example)", false, 20)] + private static void CreateAreaLight() + { + var go = GameObject.CreatePrimitive(PrimitiveType.Quad); + Undo.RegisterCreatedObjectUndo(go, "Create Bakery light"); + go.AddComponent(); + go.name = "AreaLight"; + var ecam = SceneView.lastActiveSceneView.camera.transform; + go.transform.position = ecam.position + ecam.forward; + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + var mat = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftDefaultAreaLightMat.mat", typeof(Material)) as Material; + go.GetComponent().material = mat; + var arr = new GameObject[1]; + arr[0] = go; + Selection.objects = arr; + } + + [MenuItem("Bakery/Create/Spotlight", false, 20)] + private static void CreateSpotLight() + { + var go = new GameObject(); + Undo.RegisterCreatedObjectUndo(go, "Create Bakery light"); + var light = go.AddComponent(); + light.projMode = BakeryPointLight.ftLightProjectionMode.Cookie; + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + light.cookie = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftUnitySpotTexture.bmp", typeof(Texture2D)) as Texture2D; + go.name = "SpotLight"; + var ecam = SceneView.lastActiveSceneView.camera.transform; + go.transform.position = ecam.position + ecam.forward; + var arr = new GameObject[1]; + arr[0] = go; + Selection.objects = arr; + } + + [MenuItem("Bakery/Create/Volume", false, 20)] + private static void CreateVolume() + { + var go = new GameObject(); + Undo.RegisterCreatedObjectUndo(go, "Create Bakery Volume"); + go.AddComponent(); + go.name = "BakeryVolume"; + var ecam = SceneView.lastActiveSceneView.camera.transform; + go.transform.position = ecam.position + ecam.forward; + var arr = new GameObject[1]; + arr[0] = go; + Selection.objects = arr; + } +} diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftCreateMenu.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftCreateMenu.cs.meta new file mode 100644 index 00000000..d7edddf2 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftCreateMenu.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1b37b47c815251d4290ee5b16dec9c70 +timeCreated: 1527799006 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDDS.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDDS.cs new file mode 100644 index 00000000..6bdfbc10 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDDS.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +public class ftDDS +{ + public static byte[] ddsHeaderFloat4 = new byte[] + { + 0x44, 0x44, 0x53, 0x20, 0x7C, 0x00, 0x00, 0x00, 0x07, 0x10, 0x08, 0x00, 0xF0, 0x00, 0x00, 0x00, + 0x40, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + public static byte[] ddsHeaderHalf4 = new byte[] + { + 0x44, 0x44, 0x53, 0x20, 0x7C, 0x00, 0x00, 0x00, 0x07, 0x10, 0x08, 0x00, 0xCD, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x68, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + public static byte[] ddsHeaderRGBA8 = new byte[] + { + 0x44, 0x44, 0x53, 0x20, 0x7C, 0x00, 0x00, 0x00, 0x07, 0x10, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + public static byte[] ddsHeaderR32F = new byte[] + { + 0x44, 0x44, 0x53, 0x20, 0x7C, 0x00, 0x00, 0x00, 0x07, 0x10, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; +} diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDDS.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDDS.cs.meta new file mode 100644 index 00000000..c6ae549d --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDDS.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7651ced8d6837974980b54a8c065ca41 +timeCreated: 1526839491 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDefine.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDefine.cs new file mode 100644 index 00000000..f29d6776 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDefine.cs @@ -0,0 +1,41 @@ +#if UNITY_EDITOR + +using UnityEngine; +using UnityEditor; +using System; +using UnityEditor.Build; + +[InitializeOnLoad] +#if UNITY_2017_4_OR_NEWER +public class ftDefine : IActiveBuildTargetChanged +#else +public class ftDefine +#endif +{ + static void AddDefine() + { + var platform = EditorUserBuildSettings.selectedBuildTargetGroup; + var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(platform); + if (!defines.Contains("BAKERY_INCLUDED")) + { + if (defines.Length > 0) defines += ";"; + defines += "BAKERY_INCLUDED"; + PlayerSettings.SetScriptingDefineSymbolsForGroup(platform, defines); + } + } + + static ftDefine() + { + AddDefine(); + } + +#if UNITY_2017_4_OR_NEWER + public int callbackOrder { get { return 0; } } + public void OnActiveBuildTargetChanged(BuildTarget previousTarget, BuildTarget newTarget) + { + AddDefine(); + } +#endif +} + +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDefine.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDefine.cs.meta new file mode 100644 index 00000000..5fe128a4 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDefine.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 993d44f6e3c171944a748e43ca064632 +timeCreated: 1584625781 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDetectSettings.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDetectSettings.cs new file mode 100644 index 00000000..e1cedfe8 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDetectSettings.cs @@ -0,0 +1,261 @@ +using UnityEngine; +using UnityEditor; +using System.IO; +using UnityEngine.SceneManagement; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +public class ftDetectSettings +{ + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern System.IntPtr RunLocalProcess([MarshalAs(UnmanagedType.LPWStr)]string commandline, bool setWorkDir); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern bool IsProcessFinished(System.IntPtr proc); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern int GetProcessReturnValueAndClose(System.IntPtr proc); + + [DllImport ("simpleProgressBar", CallingConvention=CallingConvention.Cdecl)] + public static extern int simpleProgressBarShow(string header, string msg, float percent, float step, bool onTop); + + [DllImport ("simpleProgressBar", CallingConvention=CallingConvention.Cdecl)] + public static extern bool simpleProgressBarCancelled(); + + [DllImport ("simpleProgressBar", CallingConvention=CallingConvention.Cdecl)] + public static extern void simpleProgressBarEnd(); + + static IEnumerator progressFunc; + static int lastReturnValue = -1; + static bool userCanceled = false; + + static bool runsRTX, runsNonRTX, runsOptix5, runsOptix6, runsOptix7, runsOIDN; + + const string progressHeader = "Detecting compatible configuration"; + + static void ShowProgress(string msg, float percent) + { + simpleProgressBarShow(progressHeader, msg, percent, 0, true); + } + + static void ValidateFileAttribs(string file) + { + var attribs = File.GetAttributes(file); + if ((attribs & FileAttributes.ReadOnly) != 0) + { + File.SetAttributes(file, attribs & ~FileAttributes.ReadOnly); + } + } + + [MenuItem("Bakery/Utilities/Detect optimal settings", false, 54)] + public static void DetectCompatSettings() + { + var bakeryPath = ftLightmaps.GetEditorPath(); + ValidateFileAttribs(bakeryPath+"/hwtestdata/image.lz4"); + + progressFunc = DetectCoroutine(); + EditorApplication.update += DetectUpdate; + } + + static IEnumerator DetectCoroutine() + { + float stages = 6; + float step = 1.0f / stages; + float progress = 0; + IEnumerator crt; + + ShowProgress("Testing: RTX ray-tracing", progress); + crt = ProcessCoroutine("ftraceRTX.exe /sun hwtestdata light 4 0 0 direct0.bin"); + while (crt.MoveNext()) yield return null; + if (userCanceled) yield break; + runsRTX = lastReturnValue==0; + progress += step; + + ShowProgress("Testing: non-RTX ray-tracing", progress); + crt = ProcessCoroutine("ftrace.exe /sun hwtestdata light 4 0 0 direct0.bin"); + while (crt.MoveNext()) yield return null; + if (userCanceled) yield break; + runsNonRTX = lastReturnValue==0; + progress += step; + + ShowProgress("Testing: OptiX 5.1 denoiser", progress); + crt = ProcessCoroutine("denoiserLegacy c hwtestdata/image.lz4 hwtestdata/image.lz4 16 0"); + while (crt.MoveNext()) yield return null; + if (userCanceled) yield break; + runsOptix5 = lastReturnValue==0; + progress += step; + + ShowProgress("Testing: OptiX 6.0 denoiser", progress); + crt = ProcessCoroutine("denoiser c hwtestdata/image.lz4 hwtestdata/image.lz4 16 0"); + while (crt.MoveNext()) yield return null; + if (userCanceled) yield break; + runsOptix6 = lastReturnValue==0; + progress += step; + + ShowProgress("Testing: OptiX 7.2 denoiser", progress); + crt = ProcessCoroutine("denoiser72 c hwtestdata/image.lz4 hwtestdata/image.lz4 16 0"); + while (crt.MoveNext()) yield return null; + if (userCanceled) yield break; + runsOptix7 = lastReturnValue==0; + progress += step; + + ShowProgress("Testing: OpenImageDenoise", progress); + crt = ProcessCoroutine("denoiserOIDN c hwtestdata/image.lz4 hwtestdata/image.lz4 16 0"); + while (crt.MoveNext()) yield return null; + if (userCanceled) yield break; + runsOIDN = lastReturnValue==0; + progress += step; + + simpleProgressBarEnd(); + + if (!runsRTX && !runsNonRTX) + { + EditorUtility.DisplayDialog("Error", "Both RTX and non-RTX lightmapper failed to run. Make sure you are using NVIDIA GPU and the drivers are up to date.", "OK"); + yield break; + } + + string str = "Testing results:\n\n"; + str += "RTX ray-tracing: " + (runsRTX ? "yes" : "no") + "\n"; + str += "Non-RTX ray-tracing: " + (runsNonRTX ? "yes" : "no") + "\n"; + str += "OptiX 5.1 denoiser: " + (runsOptix5 ? "yes" : "no") + "\n"; + str += "OptiX 6.0 denoiser: " + (runsOptix6 ? "yes" : "no") + "\n"; + str += "OptiX 7.2 denoiser: " + (runsOptix7 ? "yes" : "no") + "\n"; + str += "OpenImageDenoise: " + (runsOIDN ? "yes" : "no") + "\n"; + + str += "\n"; + str += "Recommended RTX mode: "; + if (runsRTX && runsNonRTX) + { + str += "ON if you are using a GPU with RT acceleration (e.g. 2xxx or 3xxx GeForce series), OFF otherwise.\n"; + } + else if (runsRTX) + { + str += "ON\n"; + } + else if (runsNonRTX) + { + str += "OFF\n"; + } + + str += "\n"; + str += "Recommended denoiser: "; + if (runsOptix5) + { + // OptiX 5.1 has stable quality since release, but not supported on 30XX + str += "OptiX 5.1\n"; + } + else if (runsOIDN) + { + // OIDN is stable and pretty good, but might be slower + str += "OpenImageDenoise\n"; + } + // OptiX 6 and 7.2 should run on 30XX, but quality is sometimes questionable IF driver is newer than 442.50 + // as the network is now part of the driver. + // On older drivers they should work similar to 5.1. + else if (runsOptix7) + { + str += "OptiX 7.2\n"; + } + else if (runsOptix6) + { + str += "OptiX 6.0\n"; + } + else + { + str += "all denoiser tests failed. Try updating GPU drivers.\n"; + } + + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + var gstorage = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftGlobalStorage.asset", typeof(ftGlobalStorage)) as ftGlobalStorage; + if (gstorage == null) Debug.LogError("Can't find global storage"); + var storage = ftRenderLightmap.FindRenderSettingsStorage(); + + if (gstorage != null) + { + gstorage.foundCompatibleSetup = true; + gstorage.gpuName = SystemInfo.graphicsDeviceName; + gstorage.runsNonRTX = runsNonRTX; + gstorage.alwaysEnableRTX = false; + gstorage.runsOptix5 = runsOptix5; + gstorage.runsOptix6 = runsOptix6; + gstorage.runsOptix7 = runsOptix7; + gstorage.runsOIDN = runsOIDN; + } + + if (!EditorUtility.DisplayDialog("Results", str, "OK", "Set recommended as default")) + { + if (runsRTX && runsNonRTX) + { + gstorage.renderSettingsRTXMode = EditorUtility.DisplayDialog("Question", "Does your GPU have RT cores (set RTX mode as default)?", "Yes", "No"); + } + else if (runsRTX) + { + gstorage.renderSettingsRTXMode = true; + } + else + { + gstorage.renderSettingsRTXMode = false; + } + + if (runsOptix5) + { + gstorage.renderSettingsDenoiserType = (int)ftGlobalStorage.DenoiserType.Optix5; + } + else if (runsOIDN) + { + gstorage.renderSettingsDenoiserType = (int)ftGlobalStorage.DenoiserType.OpenImageDenoise; + } + else if (runsOptix7) + { + gstorage.renderSettingsDenoiserType = (int)ftGlobalStorage.DenoiserType.Optix7; + } + else if (runsOptix6) + { + gstorage.renderSettingsDenoiserType = (int)ftGlobalStorage.DenoiserType.Optix6; + } + + EditorUtility.SetDirty(gstorage); + Debug.Log("Default settings saved"); + + if (storage != null) + { + storage.renderSettingsRTXMode = gstorage.renderSettingsRTXMode; + storage.renderSettingsDenoiserType = gstorage.renderSettingsDenoiserType; + } + } + + var bakery = ftRenderLightmap.instance != null ? ftRenderLightmap.instance : new ftRenderLightmap(); + bakery.LoadRenderSettings(); + } + + static void DetectUpdate() + { + if (!progressFunc.MoveNext()) + { + EditorApplication.update -= DetectUpdate; + } + } + + static IEnumerator ProcessCoroutine(string cmd) + { + var exeProcess = RunLocalProcess(cmd, true); + if (exeProcess == (System.IntPtr)null) + { + lastReturnValue = -1; + yield break; + } + while(!IsProcessFinished(exeProcess)) + { + yield return null; + userCanceled = simpleProgressBarCancelled(); + if (userCanceled) + { + simpleProgressBarEnd(); + yield break; + } + } + lastReturnValue = GetProcessReturnValueAndClose(exeProcess); + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDetectSettings.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDetectSettings.cs.meta new file mode 100644 index 00000000..59f732cf --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDetectSettings.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: afee4282908768e4a8b35d3e5754110c +timeCreated: 1605465718 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDirectLightInspector.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDirectLightInspector.cs new file mode 100644 index 00000000..285842d3 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDirectLightInspector.cs @@ -0,0 +1,418 @@ + +using UnityEditor; +using UnityEngine; +using System; +using System.IO; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEngine.Rendering; + +[CustomEditor(typeof(BakeryDirectLight))] +[CanEditMultipleObjects] +public class ftDirectLightInspector : UnityEditor.Editor +{ + SerializedProperty ftraceLightColor; + SerializedProperty ftraceLightIntensity; + SerializedProperty ftraceLightShadowSpread; + SerializedProperty ftraceLightSamples; + SerializedProperty ftraceLightBitmask; + SerializedProperty ftraceLightBakeToIndirect; + SerializedProperty ftraceLightShadowmask; + SerializedProperty ftraceLightShadowmaskDenoise; + SerializedProperty ftraceLightIndirectIntensity; + SerializedProperty ftraceLightTexture, ftraceLightCSTilingX, ftraceLightCSTilingY, ftraceLightCSOffsetX, ftraceLightCSOffsetY; + + ftLightmapsStorage storage; + + bool isHDRP = false; + + public enum BakeWhat + { + DirectAndIndirect = 0, + IndirectOnly = 1, + IndirectAndShadowmask = 2 + }; + + static public string[] directContributionIndirectOptions = new string[] {"Direct And Indirect", "Indirect Only", "Shadowmask and Indirect (not applicable in Indirect mode)"}; + + static string[] selStrings = new string[] {"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16", + "17","18","19","20","21","22","23","24","25","26","27","28","29","30"};//,"31"}; + + void InitSerializedProperties(SerializedObject obj) + { + ftraceLightColor = obj.FindProperty("color"); + ftraceLightIntensity = obj.FindProperty("intensity"); + ftraceLightIndirectIntensity = obj.FindProperty("indirectIntensity"); + ftraceLightShadowSpread = obj.FindProperty("shadowSpread"); + ftraceLightSamples = obj.FindProperty("samples"); + ftraceLightBitmask = obj.FindProperty("bitmask"); + ftraceLightBakeToIndirect = obj.FindProperty("bakeToIndirect"); + ftraceLightShadowmask = obj.FindProperty("shadowmask"); + ftraceLightShadowmaskDenoise = obj.FindProperty("shadowmaskDenoise"); + ftraceLightTexture = obj.FindProperty("cloudShadow"); + ftraceLightCSTilingX = obj.FindProperty("cloudShadowTilingX"); + ftraceLightCSTilingY = obj.FindProperty("cloudShadowTilingY"); + ftraceLightCSOffsetX = obj.FindProperty("cloudShadowOffsetX"); + ftraceLightCSOffsetY = obj.FindProperty("cloudShadowOffsetY"); + + isHDRP = (target as BakeryDirectLight).GetComponent("HDAdditionalLightData") != null; + } + + void OnEnable() + { + InitSerializedProperties(serializedObject); + } + + void SetHDRPLight(Light l) + { + l.intensity *= Mathf.PI; + + var hdrpLight = l.GetComponent("HDAdditionalLightData"); + if (hdrpLight == null) + { + Debug.LogWarning("HDRP: no HDAdditionalLightData"); + return; + } + var so = new SerializedObject(hdrpLight); + if (so == null) + { + Debug.LogWarning("HDRP: no SerializedObject"); + return; + } + + SerializedProperty hdrpInt2 = so.FindProperty("m_Intensity"); + if (hdrpInt2 == null) + { + Debug.LogWarning("HDRP: no m_Intensity"); + return; + } + hdrpInt2.floatValue = l.intensity; + + so.ApplyModifiedProperties(); + } + + void GetLinearLightParameters(Light light, out float lightR, out float lightG, out float lightB, out float lightInt) + { + if (PlayerSettings.colorSpace != ColorSpace.Linear) + { + lightInt = light.intensity; + lightR = light.color.r; + lightG = light.color.g; + lightB = light.color.b; + return; + } + + if (!GraphicsSettings.lightsUseLinearIntensity) + { + lightR = Mathf.Pow(light.color.r * light.intensity, 2.2f); + lightG = Mathf.Pow(light.color.g * light.intensity, 2.2f); + lightB = Mathf.Pow(light.color.b * light.intensity, 2.2f); + lightInt = Mathf.Max(Mathf.Max(lightR, lightG), lightB); + lightR /= lightInt; + lightG /= lightInt; + lightB /= lightInt; + } + else + { + lightInt = light.intensity; + lightR = light.color.linear.r; + lightG = light.color.linear.g; + lightB = light.color.linear.b; + } + } + + public override void OnInspectorGUI() { + //if (showFtrace) + //{ + OnEnable(); + serializedObject.Update(); + + EditorGUILayout.PropertyField(ftraceLightColor, new GUIContent("Color", "Color of the light")); + EditorGUILayout.PropertyField(ftraceLightIntensity, new GUIContent("Intensity", "Color multiplier (Lux / Pi)")); + EditorGUILayout.PropertyField(ftraceLightShadowSpread, new GUIContent("Shadow spread", "Controls shadow blurriness from 0 to 1")); + EditorGUILayout.PropertyField(ftraceLightSamples, new GUIContent("Shadow samples", "The amount of rays tested for this light. Rays are emitted from lightmap texel towards the light, distributed conically. Radius of the cone depends on Shadow Spread.")); + + //ftraceLightBitmask.intValue = EditorGUILayout.MaskField(new GUIContent("Bitmask", "Lights only affect renderers with overlapping bits"), ftraceLightBitmask.intValue, selStrings); + int prevVal = ftraceLightBitmask.intValue; + int newVal = EditorGUILayout.MaskField(new GUIContent("Bitmask", "Lights only affect renderers with overlapping bits"), ftraceLightBitmask.intValue, selStrings); + if (prevVal != newVal) ftraceLightBitmask.intValue = newVal; + + /* + EditorGUILayout.PropertyField(ftraceLightBakeToIndirect, new GUIContent("Bake to indirect", "Add direct contribution from this light to indirect-only lightmaps")); + if (ftraceLightBakeToIndirect.boolValue && ftraceLightShadowmask.boolValue) ftraceLightShadowmask.boolValue = false; + + EditorGUILayout.PropertyField(ftraceLightShadowmask, new GUIContent("Shadowmask", "Enable mixed lighting. Static shadows from this light will be baked, and real-time light will cast shadows from dynamic objects.")); + if (ftraceLightBakeToIndirect.boolValue && ftraceLightShadowmask.boolValue) ftraceLightBakeToIndirect.boolValue = false; + */ + + if (storage == null) storage = ftRenderLightmap.FindRenderSettingsStorage(); + var rmode = storage.renderSettingsUserRenderMode; + if (rmode != (int)ftRenderLightmap.RenderMode.FullLighting) + { + BakeWhat contrib; + if (ftraceLightShadowmask.boolValue) + { + contrib = BakeWhat.IndirectAndShadowmask; + } + else if (ftraceLightBakeToIndirect.boolValue) + { + contrib = BakeWhat.DirectAndIndirect; + } + else + { + contrib = BakeWhat.IndirectOnly; + } + var prevContrib = contrib; + + if (rmode == (int)ftRenderLightmap.RenderMode.Indirect) + { + contrib = (BakeWhat)EditorGUILayout.Popup("Baked contribution", (int)contrib, directContributionIndirectOptions); + } + else if (rmode == (int)ftRenderLightmap.RenderMode.Shadowmask) + { + contrib = (BakeWhat)EditorGUILayout.EnumPopup("Baked contribution", contrib); + } + + if (prevContrib != contrib) + { + if (contrib == BakeWhat.IndirectOnly) + { + ftraceLightShadowmask.boolValue = false; + ftraceLightBakeToIndirect.boolValue = false; + } + else if (contrib == BakeWhat.IndirectAndShadowmask) + { + ftraceLightShadowmask.boolValue = true; + ftraceLightBakeToIndirect.boolValue = false; + } + else + { + ftraceLightShadowmask.boolValue = false; + ftraceLightBakeToIndirect.boolValue = true; + } + } + + if (ftraceLightShadowmask.boolValue) + { + EditorGUILayout.PropertyField(ftraceLightShadowmaskDenoise, new GUIContent("Denoise shadowmask", "Apply denoising to shadowmask texture. For sharp shadows it may be unnecessary.")); + } + } + + EditorGUILayout.PropertyField(ftraceLightIndirectIntensity, new GUIContent("Indirect intensity", "Non-physical GI multiplier for this light")); + + EditorGUILayout.PropertyField(ftraceLightTexture, new GUIContent("Texture projection", "Tiled projected texture")); + if (ftraceLightTexture.objectReferenceValue != null) + { + EditorGUILayout.PropertyField(ftraceLightCSTilingX, new GUIContent("Tiling U", "Cloud shadow U tiling")); + EditorGUILayout.PropertyField(ftraceLightCSTilingY, new GUIContent("Tiling V", "Cloud shadow V tiling")); + EditorGUILayout.PropertyField(ftraceLightCSOffsetX, new GUIContent("Offset U", "Cloud shadow U tiling")); + EditorGUILayout.PropertyField(ftraceLightCSOffsetY, new GUIContent("Offset V", "Cloud shadow V tiling")); + } + + serializedObject.ApplyModifiedProperties(); + //} + + + bool showError = false; + string why = ""; + + bool shadowmaskNoDynamicLight = false; + + foreach(BakeryDirectLight selectedLight in targets) + { + bool match = true; + + var light = selectedLight.GetComponent(); + if (light == null) + { + if (ftraceLightShadowmask.boolValue) shadowmaskNoDynamicLight = true; + continue; + } + if (!light.enabled) + { + if (ftraceLightShadowmask.boolValue) shadowmaskNoDynamicLight = true; + } + var so = new SerializedObject(selectedLight); + InitSerializedProperties(so); + + if (light.type != LightType.Directional) + { + match = false; + why = "real-time light is not direct"; + } + + if (light.bounceIntensity != ftraceLightIndirectIntensity.floatValue) + { + match = false; + why = "indirect intensity doesn't match"; + } + + var clr = ftraceLightColor.colorValue; + float eps = 1.0f / 255.0f; + float lightR, lightG, lightB, lightInt; + float fr, fg, fb; + float fintensity = ftraceLightIntensity.floatValue; + if (isHDRP) fintensity *= Mathf.PI; + if (PlayerSettings.colorSpace == ColorSpace.Linear) + { + fr = clr.linear.r;// * fintensity; + fg = clr.linear.g;// * fintensity; + fb = clr.linear.b;// * fintensity; + } + else + { + fr = clr.r; + fg = clr.g; + fb = clr.b; + } + GetLinearLightParameters(light, out lightR, out lightG, out lightB, out lightInt); + + if (GraphicsSettings.lightsUseLinearIntensity || PlayerSettings.colorSpace != ColorSpace.Linear) + { + if (Mathf.Abs(lightR - fr) > eps || Mathf.Abs(lightG - fg) > eps || Mathf.Abs(lightB - fb) > eps) + { + match = false; + why = "color doesn't match"; + } + else if (Mathf.Abs(lightInt - fintensity) > eps) + { + match = false; + why = "intensity doesn't match"; + } + } + else + { + eps *= Mathf.Max(lightInt, fintensity); + if (Mathf.Abs(lightR*lightInt - fr*fintensity) > eps || + Mathf.Abs(lightG*lightInt - fg*fintensity) > eps || + Mathf.Abs(lightB*lightInt - fb*fintensity) > eps) + { + match = false; + why = "intensity doesn't match"; + } + } + + if (!match) + { + showError = true; + } + } + + if (shadowmaskNoDynamicLight) + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Warning: shadowmask needs enabled real-time light to work"); + } + + if (showError) + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Real-time light doesn't match lightmap: " + why); + if (GUILayout.Button("Match lightmapped to real-time")) + { + foreach(BakeryDirectLight selectedLight in targets) + { + var light = selectedLight.GetComponent(); + if (light == null) continue; + //if (!light.enabled) continue; + var so = new SerializedObject(selectedLight); + InitSerializedProperties(so); + + if (PlayerSettings.colorSpace != ColorSpace.Linear) + { + ftraceLightColor.colorValue = light.color; + ftraceLightIntensity.floatValue = light.intensity; + } + else if (!GraphicsSettings.lightsUseLinearIntensity) + { + float lightR, lightG, lightB, lightInt; + GetLinearLightParameters(light, out lightR, out lightG, out lightB, out lightInt); + ftraceLightColor.colorValue = new Color(lightR, lightG, lightB); + ftraceLightIntensity.floatValue = lightInt; + } + else + { + ftraceLightColor.colorValue = light.color; + ftraceLightIntensity.floatValue = light.intensity; + } + ftraceLightIndirectIntensity.floatValue = light.bounceIntensity; + if (isHDRP) ftraceLightIntensity.floatValue /= Mathf.PI; + + so.ApplyModifiedProperties(); + } + } + if (GUILayout.Button("Match real-time to lightmapped")) + { + foreach(BakeryDirectLight selectedLight in targets) + { + var light = selectedLight.GetComponent(); + if (light == null) continue; + //if (!light.enabled) continue; + var so = new SerializedObject(selectedLight); + InitSerializedProperties(so); + + Undo.RecordObject(light, "Change light"); + if (PlayerSettings.colorSpace != ColorSpace.Linear) + { + light.color = ftraceLightColor.colorValue; + light.intensity = ftraceLightIntensity.floatValue; + } + else if (!GraphicsSettings.lightsUseLinearIntensity) + { + float fr, fg, fb; + float fintensity = ftraceLightIntensity.floatValue; + var clr = ftraceLightColor.colorValue; + fr = clr.linear.r;// * fintensity; + fg = clr.linear.g;// * fintensity; + fb = clr.linear.b;// * fintensity; + + fr = Mathf.Pow(fr * fintensity, 1.0f / 2.2f); + fg = Mathf.Pow(fg * fintensity, 1.0f / 2.2f); + fb = Mathf.Pow(fb * fintensity, 1.0f / 2.2f); + float fint = Mathf.Max(Mathf.Max(fr, fg), fb); + fr /= fint; + fg /= fint; + fb /= fint; + light.color = new Color(fr, fg, fb); + light.intensity = fint; + } + else + { + light.color = ftraceLightColor.colorValue; + light.intensity = ftraceLightIntensity.floatValue; + } + light.type = LightType.Directional; + light.bounceIntensity = ftraceLightIndirectIntensity.floatValue; + if (isHDRP) SetHDRPLight(light); + } + } + } + + + if (PlayerSettings.colorSpace == ColorSpace.Linear) + { + if (!GraphicsSettings.lightsUseLinearIntensity) + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Warning: project is not set up to use linear light intensity."); + EditorGUILayout.LabelField("GraphicsSettings.lightsUseLinearIntensity should be TRUE."); + if (GUILayout.Button("Fix")) + { + GraphicsSettings.lightsUseLinearIntensity = true; + } + } + else + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Project is using linear light intensity. This is nice."); + if (GUILayout.Button("Change to non-linear")) + { + GraphicsSettings.lightsUseLinearIntensity = false; + } + } + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDirectLightInspector.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDirectLightInspector.cs.meta new file mode 100644 index 00000000..40009f35 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftDirectLightInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3a4eb21edcc395a419e2da3246fcbc15 +timeCreated: 1525273871 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftErrorCodes.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftErrorCodes.cs new file mode 100644 index 00000000..2f3d20c5 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftErrorCodes.cs @@ -0,0 +1,266 @@ +#if UNITY_EDITOR + +using System.Collections.Generic; + +public class ftErrorCodes +{ + static Dictionary ftraceMap = new Dictionary + { + {1, "Unknown error. See .ftracelog.txt for details."}, + {2, "Error selecting pass"}, + {5120, "Can't open lms.bin"}, + {984, "lmlod.bin doesn't match lms.bin"}, + {500, "Can't load geometry data. See .ftracelog.txt for details."}, + {501, "Can't load UVGBuffer smooth position"}, + {502, "Can't load UVGBuffer face normal"}, + {505, "Can't load trimarks.bin"}, + {5005, "Can't load sky.bin"}, + {500599, "Can't load ao.bin"}, + {5005991, "Can't load sss.bin"}, + {507, "Can't load vbtraceUV0.bin"}, + {508, "Can't load UVGBuffer tangent"}, + {550, "Can't load light data. See .ftracelog.txt for details."}, + {557, "Can't load alpha IDs. See .ftracelog.txt for details."}, + {512, "Can't load compositing data. See .ftracelog.txt for details."}, + {51298, "Can't open addao.bin"}, + {875, "Can't load heightmap. See .ftracelog.txt for details."}, + {90, "Can't load normal to compose. See .ftracelog.txt for details."}, + {91, "Can't load lightmap to compose. See .ftracelog.txt for details."}, + {909, "No enabled CUDA devices. See .ftracelog.txt for details."}, + {910, "Can't load direction to compose. See .ftracelog.txt for details."}, + {92, "Can't load lightmap to compose. See .ftracelog.txt for details."}, + {920, "Can't load lightmap to compose. See .ftracelog.txt for details."}, + {921, "Can't load emission. See .ftracelog.txt for details."}, + {93, "Can't load lightmap to compose. See .ftracelog.txt for details."}, + {94, "Can't load lightmap. See .ftracelog.txt for details."}, + {940, "Can't read direction for GI. See .ftracelog.txt for details."}, + {95, "Can't read lightmap for GI. See .ftracelog.txt for details."}, + {510, "Can't write composed lightmap. See .ftracelog.txt for details."}, + {514, "Can't write composed lightmap. See .ftracelog.txt for details."}, + {7500, "Can't load UVGBuffer normal or position"}, + {5090, "Can't decompress UVGBuffer normal"}, + {5091, "Can't decompress UVGBuffer position"}, + {5092, "Can't decompress UVGBuffer smooth position"}, + {5093, "Can't decompress UVGBuffer face normal"}, + {5083, "Can't decompress UVGBuffer tangent"}, + {7007, "Can't load direct.bin"}, + {7771, "Can't read sky texture"}, + {7772, "Can't read light texture"}, + {888, "No texture name for cubemaplight"}, + {8008, "Can't load direct lighting."}, + {1000, "Can't read albedo for GI. See .ftracelog.txt for details."}, + {1001, "Can't read lightmap for GI. See .ftracelog.txt for details."}, + {1007, "Can't read direction for GI. See .ftracelog.txt for details."}, + {1888, "Failed to initialize"}, + {10000, "Can't load gi.bin"}, + {850, "Can't open log file"} + }; + + static Dictionary combineMasksMap = new Dictionary + { + {23, "Can't load texture"}, + {501, "Can't write file. See console for details."}, + {5, "Failed to save TGA file. See console for details."}, + {55, "Failed to save PNG file. See console for details."} + }; + + static Dictionary denoiserMap = new Dictionary + { + {2, "Incorrect arguments"}, + {3, "Incorrect tile size. Must be between 64 and 8192"}, + {500, "Can't load texture. See console for details."}, + {5001, "Can't load texture. See console for details."}, + {5002, "Can't load texture. See console for details."}, + {5003, "Can't load texture. See console for details."}, + {4, "Incorrect tile size. Must be width%tile == height%tile == 0"}, + {501, "Can't write file. See console for details."}, + {505, "Unknown error (old driver?)"} + }; + + static Dictionary h2hMap = new Dictionary + { + {23, "Can't load texture. See console for details."}, + {2, "Failed to get image data from DDS. See console for details."}, + {3, "Failed to init D3D11"}, + {4, "Failed to convert"}, + {45, "Failed to transform pixels"}, + {5, "Failed to save HDR file. See console for details."} + }; + + static Dictionary i2tMap = new Dictionary + { + {1, "Incorrect arguments"}, + {2, "Can't read file. See console for details."}, + {3, "Can't write file. See console for details."}, + {4, "IES file is not valid. See console for details."}, + {5, "IES file uses unknown symmetry mode. See console for details."} + }; + + static Dictionary seamfixerMap = new Dictionary + { + {1, "Incorrect arguments"}, + {2, "Failed to init D3D11"}, + {501, "Can't load vbtraceTex.bin"}, + {10, "Can't load lms.bin"}, + {600, "Can't load lightmap"}, + {22, "Can't create D3D11 resource"}, + {3, "Can't create D3D11 resource"}, + {4, "Can't allocate RAM texture"}, + {8, "Can't save texture. See console for details."} + }; + + static Dictionary lmrMap = new Dictionary + { + {2, "Failed to init D3D11 or create resource"}, + {3, "Can't create D3D11 resource"}, + {601, "Can't load lodmask"}, + {602, "Can't decompress lodmask (unexpected size)"}, + {32, "Can't create mip texture"}, + {33, "Can't create mip render target"}, + {34, "Can't create mip shader resource view"}, + {4, "Can't allocate RAM mip texture"}, + {8, "Can't save texture"} + }; + + static Dictionary serverMap = new Dictionary + { + {ftClient.SERVERERROR_IDLE, "Idle"}, + {ftClient.SERVERERROR_COPY, "File copying failed"}, + {ftClient.SERVERERROR_GIPARAMS, "Failed to generate GI parameters"}, + {ftClient.SERVERERROR_NOTIMPLEMENTED, "Feature is not implemented"}, + {ftClient.SERVERERROR_UNKNOWNTASK, "Unknown task submitted"}, + {ftClient.SERVERERROR_SCENENAMETOOLONG, "Scene name is too long"}, + {ftClient.SERVERERROR_FILENOTFOUND, "File not found"}, + {ftClient.SERVERERROR_FILEHASZEROSIZE, "File has zero size"}, + {ftClient.SERVERERROR_NOMEM, "Out of memory"}, + {ftClient.SERVERERROR_INCORRECT, "Incorrect request"}, + {ftClient.SERVERERROR_INCORRECTFILENAME, "Incorrect filename"}, + {ftClient.SERVERERROR_WRITEFAILED, "write failed"}, + {ftClient.SERVERERROR_INCORRECTARGS, "incorrect arguments"}, + {ftClient.SERVERERROR_FILESIZE, "file size is too large"}, + {ftClient.SERVERERROR_STATUSLIMIT, "status message can't fit filename"} + }; + + static Dictionary serverAppMap = new Dictionary + { + {ftClient.SERVERTASK_FTRACE, "ftrace"}, + {ftClient.SERVERTASK_FTRACERTX, "ftraceRTX"}, + {ftClient.SERVERTASK_COMBINEMASKS, "combineMasks"}, + + {ftClient.SERVERTASK_DENOISE5, "denoiserLegacy"}, + {ftClient.SERVERTASK_DENOISE6, "denoiser"}, + {ftClient.SERVERTASK_DENOISE7, "denoiser72"}, + {ftClient.SERVERTASK_DENOISEOIDN, "denoiserOIDN"}, + + {ftClient.SERVERTASK_HF2HDR, "halffloat2hdr"}, + {ftClient.SERVERTASK_RGBA2TGA, "rgba2tga"}, + {ftClient.SERVERTASK_SEAMFIX, "seamfixer"} + }; + + public static string TranslateFtrace(int code, bool rtx) + { + bool unknown = false; + string text; + if (!ftraceMap.TryGetValue(code, out text)) + { + unknown = true; + text = "Unknown error"; + } + text += " (" + code + ")"; + if (unknown || code == 1) + { + text += "\n\nPossibly incompatible RTX mode? Try running Bakery -> Utilities -> Detect optimal settings."; + } + return text; + } + + public static string TranslateCombineMasks(int code) + { + string text; + if (!combineMasksMap.TryGetValue(code, out text)) text = "Unknown error"; + return text + " (" + code + ")"; + } + + public static string TranslateDenoiser(int code) + { + string text; + if (!denoiserMap.TryGetValue(code, out text)) text = "Unknown error"; + return text + " (" + code + ")"; + } + + public static string TranslateH2H(int code) + { + string text; + if (!h2hMap.TryGetValue(code, out text)) text = "Unknown error"; + return text + " (" + code + ")"; + } + + public static string TranslateI2T(int code) + { + string text; + if (!i2tMap.TryGetValue(code, out text)) text = "Unknown error"; + return text + " (" + code + ")"; + } + + public static string TranslateSeamfixer(int code) + { + string text; + if (!seamfixerMap.TryGetValue(code, out text)) text = "Unknown error"; + return text + " (" + code + ")"; + } + + public static string TranslateLMRebake(int code) + { + string text; + if (!lmrMap.TryGetValue(code, out text)) text = "Unknown error"; + return text + " (" + code + ")"; + } + + public static string TranslateServerApp(int app) + { + string text; + if (!serverAppMap.TryGetValue(app, out text)) text = "Unknown executable " + " (" + app + ")"; + return text; + } + + public static string TranslateServer(int code, int app=0, int appCode=0) + { + string text; + if (code == ftClient.SERVERERROR_BUSY) + { + text = "Busy (" + app + "/" + appCode + ")"; + } + else if (code == ftClient.SERVERERROR_APPERR) + { + var appName = TranslateServerApp(app); + text = appName + " error: " + Translate(appName, appCode); + } + else if (code == ftClient.SERVERERROR_EXEC) + { + text = "Failed to run " + TranslateServerApp(app); + } + else + { + if (!serverMap.TryGetValue(code, out text)) text = "Unknown error (" + code + ")"; + } + return text; + } + + public static string Translate(string app, int code) + { + if (app == "ftrace") return TranslateFtrace(code, false); + if (app == "ftraceRTX") return TranslateFtrace(code, true); + if (app == "combineMasks") return TranslateCombineMasks(code); + if (app == "denoiser") return TranslateDenoiser(code); + if (app == "denoiser72") return TranslateDenoiser(code); + if (app == "denoiserLegacy") return TranslateDenoiser(code); + if (app == "denoiserOIDN") return TranslateDenoiser(code); + if (app == "halffloat2hdr") return TranslateH2H(code); + if (app == "ies2tex") return TranslateI2T(code); + if (app == "rgba2tga") return TranslateCombineMasks(code); + if (app == "seamfixer") return TranslateSeamfixer(code); + return ""+code; + } +} + +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftErrorCodes.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftErrorCodes.cs.meta new file mode 100644 index 00000000..6aedab2b --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftErrorCodes.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 54cb23fa553d8b4479e0374ee0f9c502 +timeCreated: 1540538557 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftFixResettingGlobalsOnSave.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftFixResettingGlobalsOnSave.cs new file mode 100644 index 00000000..34375f4c --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftFixResettingGlobalsOnSave.cs @@ -0,0 +1,32 @@ +#if UNITY_EDITOR + +// Disable 'obsolete' warnings +#pragma warning disable 0618 + +using UnityEngine; +using UnityEditor; +using System.Collections; + +// For reasons unknown Unity will reset all shader variables set by Shader.SetGlobal... if you save a scene +// So here is a hack to fix it +public class ftFixResettingsGlobalsOnSave : SaveAssetsProcessor +{ + static void ProcUpdate() + { + if (BakeryVolume.globalVolume != null) BakeryVolume.globalVolume.OnEnable(); // set global volume again + EditorApplication.update -= ProcUpdate; // remove the callback + } + + static string[] OnWillSaveAssets(string[] paths) + { + // Only do anything if there is a global volume in the scene + if (BakeryVolume.globalVolume != null) + { + EditorApplication.update += ProcUpdate; // wait for the next editor update + } + return paths; + } +} + +#endif + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftFixResettingGlobalsOnSave.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftFixResettingGlobalsOnSave.cs.meta new file mode 100644 index 00000000..1990ff48 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftFixResettingGlobalsOnSave.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: bd16f24f4abb61548aeac9a94c816e3a +timeCreated: 1606027586 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupInspector.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupInspector.cs new file mode 100644 index 00000000..a1946203 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupInspector.cs @@ -0,0 +1,94 @@ + +using UnityEditor; +using UnityEngine; +using System; +using System.IO; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +[CustomEditor(typeof(BakeryLightmapGroup))] +[CanEditMultipleObjects] +public class ftLMGroupInspector : UnityEditor.Editor +{ + SerializedProperty ftraceResolution; + SerializedProperty ftraceMode; + SerializedProperty ftraceRenderMode; + SerializedProperty ftraceRenderDirMode; + SerializedProperty ftraceAtlasPacker; + SerializedProperty ftraceBitmask; + SerializedProperty ftraceThickness; + SerializedProperty ftraceSSS; + SerializedProperty ftraceSSSSamples; + SerializedProperty ftraceSSSDensity; + SerializedProperty ftraceSSSColor; + SerializedProperty ftraceFakeShadowBias; + SerializedProperty ftraceTransparentSelfShadow; + SerializedProperty ftraceFlipNormal; + SerializedProperty ftraceSSSScale; + + static string[] selStrings = new string[] {"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16", + "17","18","19","20","21","22","23","24","25","26","27","28","29","30"};//,"31"}; + + void OnEnable() + { + ftraceResolution = serializedObject.FindProperty("resolution"); + ftraceMode = serializedObject.FindProperty("mode"); + ftraceRenderMode = serializedObject.FindProperty("renderMode"); + ftraceRenderDirMode = serializedObject.FindProperty("renderDirMode"); + ftraceAtlasPacker = serializedObject.FindProperty("atlasPacker"); + ftraceBitmask = serializedObject.FindProperty("bitmask"); + //ftraceThickness = serializedObject.FindProperty("aoIsThickness"); + ftraceSSS = serializedObject.FindProperty("computeSSS"); + ftraceSSSSamples = serializedObject.FindProperty("sssSamples"); + ftraceSSSDensity = serializedObject.FindProperty("sssDensity"); + ftraceSSSColor = serializedObject.FindProperty("sssColor"); + ftraceSSSScale = serializedObject.FindProperty("sssScale"); + ftraceFakeShadowBias = serializedObject.FindProperty("fakeShadowBias"); + ftraceTransparentSelfShadow = serializedObject.FindProperty("transparentSelfShadow"); + ftraceFlipNormal = serializedObject.FindProperty("flipNormal"); + } + + public override void OnInspectorGUI() { + serializedObject.Update(); + + EditorGUILayout.LabelField("Bakery lightmap group parameters"); + EditorGUILayout.Space(); + + if (ftraceMode.intValue != 2) + { + var prev = ftraceResolution.intValue; + ftraceResolution.intValue = (int)Mathf.ClosestPowerOfTwo(EditorGUILayout.IntSlider("Resolution", ftraceResolution.intValue, 1, 8192)); + if (ftraceResolution.intValue != prev) EditorUtility.SetDirty(target); + } + + EditorGUILayout.PropertyField(ftraceMode, new GUIContent("Packing mode", "Determines how lightmaps are packed. In Simple mode they are not packed, and all objects sharing this group are drawn on top of each other. This is desired in case they were all unwrapped together and do not overlap. If UVs of different objects overlap, choose PackAtlas to arrange their lightmaps together into a single packed atlas.")); + + EditorGUILayout.PropertyField(ftraceRenderMode, new GUIContent("Render Mode", "")); + + EditorGUILayout.PropertyField(ftraceRenderDirMode, new GUIContent("Directional mode", "")); + + EditorGUILayout.PropertyField(ftraceAtlasPacker, new GUIContent("Atlas packer", "")); + + ftraceBitmask.intValue = EditorGUILayout.MaskField(new GUIContent("Bitmask", "Lights only affect renderers with overlapping bits"), ftraceBitmask.intValue, selStrings); + + EditorGUILayout.LabelField(""); + EditorGUILayout.LabelField("Experimental"); + + //EditorGUILayout.PropertyField(ftraceThickness, new GUIContent("Calculate AO as thickness", "")); + EditorGUILayout.PropertyField(ftraceSSS, new GUIContent("Subsurface scattering", "")); + if (ftraceSSS.boolValue) + { + EditorGUILayout.PropertyField(ftraceSSSSamples, new GUIContent("Samples", "")); + EditorGUILayout.PropertyField(ftraceSSSDensity, new GUIContent("Density", "")); + EditorGUILayout.PropertyField(ftraceSSSColor, new GUIContent("Color", "")); + EditorGUILayout.PropertyField(ftraceSSSScale, new GUIContent("Scale", "")); + } + + EditorGUILayout.PropertyField(ftraceFakeShadowBias, new GUIContent("Normal offset", "Fake normal offset for surface samples. Might be useful when applying very strong normal maps.")); + EditorGUILayout.PropertyField(ftraceTransparentSelfShadow, new GUIContent("Transparent selfshadow", "Start rays behind the surface so it doesn't cast shadows on self. Might be useful for translucent foliage.")); + EditorGUILayout.PropertyField(ftraceFlipNormal, new GUIContent("Flip normal", "Treat faces as flipped.")); + + serializedObject.ApplyModifiedProperties(); + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupInspector.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupInspector.cs.meta new file mode 100644 index 00000000..8b07c862 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c93be7cc95a299b4391dc97ea53e9348 +timeCreated: 1526381774 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupSelectorInspector.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupSelectorInspector.cs new file mode 100644 index 00000000..530532e7 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupSelectorInspector.cs @@ -0,0 +1,166 @@ + +using UnityEditor; +using UnityEngine; +using System; +using System.IO; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; + +[CustomEditor(typeof(BakeryLightmapGroupSelector))] +[CanEditMultipleObjects] +public class ftLMGroupSelectorInspector : UnityEditor.Editor +{ + SerializedProperty ftraceAsset; + SerializedProperty ftraceOverride; + SerializedProperty ftraceResolution; + + string newName = null; + int newRes = 512; + int newMask = 1; + BakeryLightmapGroup.ftLMGroupMode newMode = BakeryLightmapGroup.ftLMGroupMode.PackAtlas; + BakeryLightmapGroup.RenderDirMode newDirMode = BakeryLightmapGroup.RenderDirMode.Auto; + + static string[] selStrings = new string[] {"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16", + "17","18","19","20","21","22","23","24","25","26","27","28","29","30"};//,"31"}; + + void OnEnable() + { + ftraceAsset = serializedObject.FindProperty("lmgroupAsset"); + ftraceOverride = serializedObject.FindProperty("instanceResolutionOverride"); + ftraceResolution = serializedObject.FindProperty("instanceResolution"); + } + + void ForceSavePrefabOverride(UnityEngine.Object[] targets) + { +#if UNITY_2018_3_OR_NEWER + foreach(BakeryLightmapGroupSelector obj in targets) + { + PrefabUtility.RecordPrefabInstancePropertyModifications(obj); + EditorUtility.SetDirty(obj); + } +#endif + } + + public override void OnInspectorGUI() { + serializedObject.Update(); + + //if (!ftraceAsset.hasMultipleDifferentValues) + { + EditorGUILayout.LabelField("These lightmap parameters affect the object and its children"); + + EditorGUI.BeginChangeCheck(); + var selectedLMGroup = EditorGUILayout.ObjectField(new GUIContent("Lightmap group", "Select ftrace lightmap group asset"), + ftraceAsset.objectReferenceValue, typeof(BakeryLightmapGroup), false); + var changed = EditorGUI.EndChangeCheck(); + + if (ftraceAsset.hasMultipleDifferentValues) EditorGUILayout.LabelField("(Different values in selection)"); + + if (changed) + { + foreach(BakeryLightmapGroupSelector obj in targets) + { + Undo.RecordObject(obj, "Change LMGroup"); + obj.lmgroupAsset = selectedLMGroup; + ForceSavePrefabOverride(targets); + } + } + + if (ftraceAsset.objectReferenceValue != null) + { + var group = ftraceAsset.objectReferenceValue as BakeryLightmapGroup; + + if (group.mode != BakeryLightmapGroup.ftLMGroupMode.PackAtlas && ftraceOverride.boolValue) + { + ftraceOverride.boolValue = false; + ForceSavePrefabOverride(targets); + } + + //EditorGUILayout.LabelField("Packed atlas: " + (group.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas ? "yes" : "no")); + var modeString = "Packing: "; + if (group.mode == BakeryLightmapGroup.ftLMGroupMode.OriginalUV) { + modeString += "original UV"; + } else if (group.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas) { + modeString += "packed atlas"; + } else { + modeString += "vertex"; + } + EditorGUILayout.LabelField(modeString); + + modeString = "Directional: "; + if (group.renderDirMode == BakeryLightmapGroup.RenderDirMode.Auto) { + modeString += "auto"; + } else if (group.renderDirMode == BakeryLightmapGroup.RenderDirMode.None) { + modeString += "none"; + } else if (group.renderDirMode == BakeryLightmapGroup.RenderDirMode.BakedNormalMaps) { + modeString += "baked normal maps"; + } else if (group.renderDirMode == BakeryLightmapGroup.RenderDirMode.DominantDirection) { + modeString += "dominant direction"; + } else if (group.renderDirMode == BakeryLightmapGroup.RenderDirMode.RNM) { + modeString += "RNM"; + } else if (group.renderDirMode == BakeryLightmapGroup.RenderDirMode.SH) { + modeString += "SH"; + } + EditorGUILayout.LabelField(modeString); + + if (group.mode != BakeryLightmapGroup.ftLMGroupMode.Vertex) + { + EditorGUILayout.LabelField("Resolution: " + (ftraceOverride.boolValue ? (ftraceResolution.intValue + " (atlas: " + group.resolution + ")") : (group.resolution)+"")); + } + + if (group.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas) + { + EditorGUILayout.PropertyField(ftraceOverride, new GUIContent("Override resolution", "Manually set the resolution of this object in the atlas")); + if (ftraceOverride.boolValue) + { + ftraceResolution.intValue = EditorGUILayout.IntSlider("Resolution", ftraceResolution.intValue, 1, 8192); + ForceSavePrefabOverride(targets); + } + } + } + else + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Create new lightmap group:"); + if (newName == null) newName = "LMGroup_" + target.name; + newName = EditorGUILayout.TextField("Name", newName); + EditorGUILayout.PrefixLabel("Packing mode"); + newMode = (BakeryLightmapGroup.ftLMGroupMode)EditorGUILayout.EnumPopup(newMode); + if (newMode != BakeryLightmapGroup.ftLMGroupMode.Vertex) + { + newRes = (int)Mathf.ClosestPowerOfTwo(EditorGUILayout.IntSlider("Resolution", newRes, 1, 8192)); + } + EditorGUILayout.PrefixLabel("Directional mode"); + newDirMode = (BakeryLightmapGroup.RenderDirMode)EditorGUILayout.EnumPopup(newDirMode); + newMask = EditorGUILayout.MaskField(new GUIContent("Bitmask", "Lights only affect renderers with overlapping bits"), newMask, selStrings); + if (GUILayout.Button("Create new")) + { + BakeryLightmapGroup newGroup = ScriptableObject.CreateInstance(); + newGroup.resolution = newRes; + newGroup.bitmask = newMask; + newGroup.mode = newMode; + newGroup.renderDirMode = newDirMode; + + string fname; + var activeScene = SceneManager.GetActiveScene(); + if (activeScene.path.Length > 0) + { + fname = Path.GetDirectoryName(activeScene.path) + "/" + newName; + } + else + { + fname = "Assets/" + newName; + } + + AssetDatabase.CreateAsset(newGroup, fname + ".asset"); + AssetDatabase.SaveAssets(); + ftraceAsset.objectReferenceValue = newGroup; + } + } + } + + serializedObject.ApplyModifiedProperties(); + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupSelectorInspector.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupSelectorInspector.cs.meta new file mode 100644 index 00000000..d3075eb9 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLMGroupSelectorInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 84f0a0db10ed05349987b7b2a49c345e +timeCreated: 1526384098 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightMeshInspector.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightMeshInspector.cs new file mode 100644 index 00000000..0d1368ff --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightMeshInspector.cs @@ -0,0 +1,671 @@ + +using UnityEditor; +using UnityEngine; +using System; +using System.IO; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEngine.Rendering; + +[CustomEditor(typeof(BakeryLightMesh))] +[CanEditMultipleObjects] +public class ftLightMeshInspector : UnityEditor.Editor +{ + SerializedProperty ftraceLightColor; + SerializedProperty ftraceLightIntensity; + SerializedProperty ftraceLightIndirectIntensity; + SerializedProperty ftraceLightTexture; + SerializedProperty ftraceLightCutoff; + SerializedProperty ftraceLightSamples; + SerializedProperty ftraceLightSamples2; + SerializedProperty ftraceLightBitmask; + SerializedProperty ftraceLightSelfShadow; + SerializedProperty ftraceLightBakeToIndirect; + + static string ftLightShaderName = "Bakery/Light"; + + ftLightmapsStorage storage; + + int texCached = -1; + + static string[] selStrings = new string[] {"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16", + "17","18","19","20","21","22","23","24","25","26","27","28","29","30"};//,"31"}; + + void InitSerializedProperties(SerializedObject obj) + { + ftraceLightColor = obj.FindProperty("color"); + ftraceLightTexture = obj.FindProperty("texture"); + ftraceLightIntensity = obj.FindProperty("intensity"); + ftraceLightIndirectIntensity = obj.FindProperty("indirectIntensity"); + ftraceLightCutoff = obj.FindProperty("cutoff"); + ftraceLightSamples = obj.FindProperty("samples"); + ftraceLightSamples2 = obj.FindProperty("samples2"); + ftraceLightBitmask = obj.FindProperty("bitmask"); + ftraceLightSelfShadow = obj.FindProperty("selfShadow"); + ftraceLightBakeToIndirect = obj.FindProperty("bakeToIndirect"); + } + + void OnEnable() + { + InitSerializedProperties(serializedObject); + } + + void TestPreviewRefreshProperty(ref int cached, int newVal) + { + if (cached >= 0) + { + if (cached != newVal) + { + BakeryLightMesh.lightsChanged = 2; + } + } + cached = newVal; + } + + void TestPreviewRefreshProperty(ref int cached, UnityEngine.Object newVal) + { + if (newVal == null) + { + TestPreviewRefreshProperty(ref cached, 0); + return; + } + TestPreviewRefreshProperty(ref cached, newVal.GetInstanceID()); + } + + void GetLinearLightParameters(Light light, out float lightR, out float lightG, out float lightB, out float lightInt) + { + if (PlayerSettings.colorSpace != ColorSpace.Linear) + { + lightInt = light.intensity; + lightR = light.color.r; + lightG = light.color.g; + lightB = light.color.b; + return; + } + + if (!GraphicsSettings.lightsUseLinearIntensity) + { + lightR = Mathf.Pow(light.color.r * light.intensity, 2.2f); + lightG = Mathf.Pow(light.color.g * light.intensity, 2.2f); + lightB = Mathf.Pow(light.color.b * light.intensity, 2.2f); + lightInt = Mathf.Max(Mathf.Max(lightR, lightG), lightB); + lightR /= lightInt; + lightG /= lightInt; + lightB /= lightInt; + } + else + { + lightInt = light.intensity; + lightR = light.color.linear.r; + lightG = light.color.linear.g; + lightB = light.color.linear.b; + } + } + + public static Vector2 GetAreaLightSize(Light obj) + { + Vector2 areaSize = obj.areaSize; + + var hdrpLight = obj.GetComponent("HDAdditionalLightData"); + if (hdrpLight != null) + { + var so = new SerializedObject(hdrpLight); + if (so != null) + { + var hdrpLightTypeExtent = so.FindProperty("m_PointlightHDType"); + var hdrpLightTypeExtent2 = so.FindProperty("m_AreaLightShape"); + if (hdrpLightTypeExtent != null && hdrpLightTypeExtent2 != null) + { + int extendedLightType = hdrpLightTypeExtent.intValue; + int extendedLightType2 = hdrpLightTypeExtent2.intValue; + if (extendedLightType == 1 && // area + extendedLightType2 == 0) // rectangle + { + var hdrpLightShapeWidth = so.FindProperty("m_ShapeWidth"); + var hdrpLightShapeHeight = so.FindProperty("m_ShapeHeight"); + areaSize = new Vector2(hdrpLightShapeWidth != null ? hdrpLightShapeWidth.floatValue : 1, + hdrpLightShapeHeight != null ? hdrpLightShapeHeight.floatValue : 1); + } + else + { + Debug.LogError(obj.name + " HDRP light type unsupported: " + extendedLightType + ", " + extendedLightType2); + } + } + } + } + return areaSize; + } + + public static Vector3[] GetAreaLightCorners(Light obj) + { + var areaSize = GetAreaLightSize(obj); + + var t = obj.transform; + var pos = t.position; + var right = t.right; + var up = t.up; + var extents = areaSize * 0.5f; + var corners = new Vector3[4]; + corners[0] = pos - right * extents.x - up * extents.y; + corners[1] = pos - right * extents.x + up * extents.y; + corners[2] = pos + right * extents.x + up * extents.y; + corners[3] = pos + right * extents.x - up * extents.y; + + return corners; + } + + public static bool IsArea(Light obj) + { + var hdrpLight = obj.GetComponent("HDAdditionalLightData"); + if (hdrpLight != null) + { + var so = new SerializedObject(hdrpLight); + if (so != null) + { + var hdrpLightTypeExtent = so.FindProperty("m_PointlightHDType"); + var hdrpLightTypeExtent2 = so.FindProperty("m_AreaLightShape"); + if (hdrpLightTypeExtent != null && hdrpLightTypeExtent2 != null) + { + int extendedLightType = hdrpLightTypeExtent.intValue; + int extendedLightType2 = hdrpLightTypeExtent2.intValue; + if (extendedLightType == 1 && // area + extendedLightType2 == 0) // rectangle + { + return true; + } + } + } + } + else + { + return obj.type == LightType.Area; + } + return false; + } + + public override void OnInspectorGUI() { + //if (showFtrace) + { + OnEnable(); + serializedObject.Update(); + + TestPreviewRefreshProperty(ref texCached, ftraceLightTexture.objectReferenceValue); + + EditorGUILayout.PropertyField(ftraceLightColor, new GUIContent("Color", "Color of the light")); + EditorGUILayout.PropertyField(ftraceLightIntensity, new GUIContent("Intensity", "Color multiplier")); + EditorGUILayout.PropertyField(ftraceLightTexture, new GUIContent("Texture", "Texture")); + EditorGUILayout.PropertyField(ftraceLightCutoff, new GUIContent("Cutoff", "Lighting distance limit. For maximum physical corectness set to a very high value. Using smaller values is useful for faster render times and to match real-time lights. Bakery uses Skyforge falloff to maintain balance between correct inverse-squared attenuation and practical limits (https://habr.com/company/mailru/blog/248873/)")); + + if (ftraceLightSelfShadow.boolValue) + { + EditorGUILayout.PropertyField(ftraceLightSamples2, new GUIContent("Samples Near", "The amount of rays traced hemispherically in the proximity of this mesh. Set to 0 to only trace with 'Samples Far'.")); + } + else + { + ftraceLightSamples2.intValue = 0; + } + EditorGUILayout.PropertyField(ftraceLightSamples, new GUIContent("Samples Far", "The amount of sample points generated on the surface of this mesh. Distant mesh lights are approximated as clouds of directed half-point lights.")); + + //ftraceLightBitmask.intValue = EditorGUILayout.MaskField(new GUIContent("Bitmask", "Lights only affect renderers with overlapping bits"), ftraceLightBitmask.intValue, selStrings); + int prevVal = ftraceLightBitmask.intValue; + int newVal = EditorGUILayout.MaskField(new GUIContent("Bitmask", "Lights only affect renderers with overlapping bits"), ftraceLightBitmask.intValue, selStrings); + if (prevVal != newVal) ftraceLightBitmask.intValue = newVal; + + EditorGUILayout.PropertyField(ftraceLightSelfShadow, new GUIContent("Self shadow", "Determines if light mesh itself casts shadows.")); + + //EditorGUILayout.PropertyField(ftraceLightBakeToIndirect, new GUIContent("Bake to indirect", "Add direct contribution from this light to indirect-only lightmaps")); + + if (storage == null) storage = ftRenderLightmap.FindRenderSettingsStorage(); + var rmode = storage.renderSettingsUserRenderMode; + if (rmode != (int)ftRenderLightmap.RenderMode.FullLighting) + { + ftDirectLightInspector.BakeWhat contrib; + if (ftraceLightBakeToIndirect.boolValue) + { + contrib = ftDirectLightInspector.BakeWhat.DirectAndIndirect; + } + else + { + contrib = ftDirectLightInspector.BakeWhat.IndirectOnly; + } + var prevContrib = contrib; + + contrib = (ftDirectLightInspector.BakeWhat)EditorGUILayout.Popup("Baked contribution", (int)contrib, ftSkyLightInspector.directContributionOptions); + + if (prevContrib != contrib) + { + if (contrib == ftDirectLightInspector.BakeWhat.IndirectOnly) + { + ftraceLightBakeToIndirect.boolValue = false; + } + else + { + ftraceLightBakeToIndirect.boolValue = true; + } + } + } + + EditorGUILayout.PropertyField(ftraceLightIndirectIntensity, new GUIContent("Indirect intensity", "Non-physical GI multiplier for this light")); + + serializedObject.ApplyModifiedProperties(); + } + + bool showError = false; + string showErrorText = ""; + bool isAreaLight = false; + bool isMesh = false; + + var materialValid = new bool[targets.Length]; + int iterator = -1; + int numMaterialValid = targets.Length; + + foreach(BakeryLightMesh selectedLight in targets) + { + iterator++; + var so = new SerializedObject(selectedLight); + InitSerializedProperties(so); + + var mr = selectedLight.GetComponent(); + var mf = selectedLight.GetComponent(); + var areaLight = selectedLight.GetComponent(); + if (areaLight != null && !IsArea(areaLight)) areaLight = null; + + if (mr == null && areaLight == null) + { + showError = true; + showErrorText = "Error: no mesh renderer"; + continue; + } + + if (mf == null && areaLight == null) + { + showError = true; + showErrorText = "Error: no mesh filter"; + continue; + } + + float intensity = ftraceLightIntensity.floatValue; + var clr = ftraceLightColor.colorValue; + + if (areaLight != null) + { + bool match = true; + string why = ""; + isAreaLight = true; + + float eps = 1.0f / 255.0f; + float lightR, lightG, lightB, lightInt; + float fr, fg, fb; + if (PlayerSettings.colorSpace == ColorSpace.Linear) + { + fr = clr.linear.r;// * fintensity; + fg = clr.linear.g;// * fintensity; + fb = clr.linear.b;// * fintensity; + } + else + { + fr = clr.r; + fg = clr.g; + fb = clr.b; + } + GetLinearLightParameters(areaLight, out lightR, out lightG, out lightB, out lightInt); + + if (GraphicsSettings.lightsUseLinearIntensity || PlayerSettings.colorSpace != ColorSpace.Linear) + { + if (Mathf.Abs(lightR - fr) > eps || Mathf.Abs(lightG - fg) > eps || Mathf.Abs(lightB - fb) > eps) + { + match = false; + why = "color doesn't match"; + } + else if (Mathf.Abs(lightInt - intensity) > eps) + { + match = false; + why = "intensity doesn't match"; + } + } + else + { + eps *= Mathf.Max(lightInt, intensity); + if (Mathf.Abs(lightR*lightInt - fr*intensity) > eps || + Mathf.Abs(lightG*lightInt - fg*intensity) > eps || + Mathf.Abs(lightB*lightInt - fb*intensity) > eps) + { + match = false; + why = "intensity doesn't match"; + } + } + + if (Mathf.Abs(ftraceLightCutoff.floatValue - areaLight.range * 1.5f) > 0.01f) + { + match = false; + why = "range doesn't match"; + } + + if (ftraceLightSelfShadow.boolValue) + { + match = false; + why = "area light is not self-shadowed."; + } + + if (areaLight.bounceIntensity != ftraceLightIndirectIntensity.floatValue) + { + match = false; + why = "indirect intensity doesn't match"; + } + + if (!match) + { + //EditorGUILayout.Space(); + //EditorGUILayout.LabelField("Real-time light doesn't match lightmap: " + why); + showError = true; + showErrorText = "Area light doesn't match lightmap: " + why; + } + + continue; + } + + materialValid[iterator] = true; + Material singleMat = null; + var mats = mr.sharedMaterials; + + if (mats.Length == 0 || mats[0] == null) + { + showError = true; + showErrorText = "Error: no materials set"; + continue; + } + + isMesh = true; + + for(int i=0; i 1 && !usesftlight) + { + showError = true; + showErrorText = "Warning: intensity > 1, but not using Bakery Light shader"; + //match = false; + break; + } + var mclr = mat.HasProperty("_Color") ? mat.color : Color.white; + float eps = 0.5f/255.0f; + if (Mathf.Abs(mclr.r - clr.r) > eps || Mathf.Abs(mclr.g - clr.g) > eps || Mathf.Abs(mclr.b - clr.b) > eps) + { + showError = true; + showErrorText = "Error: light color doesn't match material color"; + //match = false; + break; + } + if (usesftlight && Mathf.Abs(mat.GetFloat("intensity") - intensity) > 0.001f) + { + showError = true; + showErrorText = "Error: light intensity doesn't match material intensity"; + //match = false; + break; + } + if (ftraceLightTexture.objectReferenceValue == null && mat.HasProperty("_MainTex") && mat.GetTexture("_MainTex")!=null) + { + showError = true; + showErrorText = "Error: textures don't match"; + //match = false; + break; + } + if (ftraceLightTexture.objectReferenceValue != null && (!mat.HasProperty("_MainTex") || mat.GetTexture("_MainTex") != ftraceLightTexture.objectReferenceValue)) + { + showError = true; + showErrorText = "Error: textures don't match"; + //match = false; + break; + } + } + + //if (match) return; + } + + + if (showError) + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField(showErrorText); + EditorGUILayout.Space(); + + string txt; + if (numMaterialValid > 0) + { + if (isMesh && !isAreaLight) + { + txt = "Match light to material"; + } + else if (!isMesh && isAreaLight) + { + txt = "Match lightmapped to area light"; + } + else + { + txt = "Match lights to meshes/area lights"; + } + if (GUILayout.Button(txt)) + { + //iterator = 0; + foreach(BakeryLightMesh selectedLight in targets) + { + //iterator++; + var so = new SerializedObject(selectedLight); + InitSerializedProperties(so); + + var mr = selectedLight.GetComponent(); + var areaLight = selectedLight.GetComponent(); + if (mr == null && areaLight == null) continue; + + if (areaLight != null) + { + if (PlayerSettings.colorSpace != ColorSpace.Linear) + { + ftraceLightColor.colorValue = areaLight.color; + ftraceLightIntensity.floatValue = areaLight.intensity; + } + else if (!GraphicsSettings.lightsUseLinearIntensity) + { + float lightR, lightG, lightB, lightInt; + GetLinearLightParameters(areaLight, out lightR, out lightG, out lightB, out lightInt); + ftraceLightColor.colorValue = new Color(lightR, lightG, lightB); + ftraceLightIntensity.floatValue = lightInt; + } + else + { + ftraceLightColor.colorValue = areaLight.color; + ftraceLightIntensity.floatValue = areaLight.intensity; + } + ftraceLightCutoff.floatValue = areaLight.range * 1.5f; + ftraceLightSelfShadow.boolValue = false; + ftraceLightIndirectIntensity.floatValue = areaLight.bounceIntensity; + so.ApplyModifiedProperties(); + continue; + } + + var mats = mr.sharedMaterials; + if (mats.Length == 0 || mats[0] == null) continue; + + var mat = mats[0]; + if (mat.shader.name == ftLightShaderName) + { + ftraceLightTexture.objectReferenceValue = mat.mainTexture; + ftraceLightColor.colorValue = mat.color; + ftraceLightIntensity.floatValue = mat.GetFloat("intensity"); + } + else if (mat.shader.name == "Unlit/Color") + { + ftraceLightTexture.objectReferenceValue = null; + ftraceLightColor.colorValue = mat.color; + ftraceLightIntensity.floatValue = 1; + } + else if (mat.shader.name == "Unlit/Texture") + { + ftraceLightTexture.objectReferenceValue = mat.mainTexture; + ftraceLightColor.colorValue = Color.white;//mat.color; + ftraceLightIntensity.floatValue = 1; + } + so.ApplyModifiedProperties(); + } + } + } + + //if (mats.Length == 0) return; + //if (mats[0] == null) return; + + if (isMesh && !isAreaLight) + { + txt = "Match material to light"; + } + else if (!isMesh && isAreaLight) + { + txt = "Match area light to lightmapped"; + } + else + { + txt = "Match meshes/area lights to lightmapped"; + } + if (GUILayout.Button(txt)) + { + foreach(BakeryLightMesh selectedLight in targets) + { + //iterator++; + var so = new SerializedObject(selectedLight); + InitSerializedProperties(so); + + var mr = selectedLight.GetComponent(); + var areaLight = selectedLight.GetComponent(); + if (mr == null && areaLight == null) continue; + + if (areaLight != null) + { + Undo.RecordObject(areaLight, "Change light"); + if (PlayerSettings.colorSpace != ColorSpace.Linear) + { + areaLight.color = ftraceLightColor.colorValue; + areaLight.intensity = ftraceLightIntensity.floatValue; + } + else if (!GraphicsSettings.lightsUseLinearIntensity) + { + var clr = ftraceLightColor.colorValue; + float fintensity = ftraceLightIntensity.floatValue; + float fr = clr.linear.r;// * fintensity; + float fg = clr.linear.g;// * fintensity; + float fb = clr.linear.b;// * fintensity; + + fr = Mathf.Pow(fr * fintensity, 1.0f / 2.2f); + fg = Mathf.Pow(fg * fintensity, 1.0f / 2.2f); + fb = Mathf.Pow(fb * fintensity, 1.0f / 2.2f); + float fint = Mathf.Max(Mathf.Max(fr, fg), fb); + fr /= fint; + fg /= fint; + fb /= fint; + areaLight.color = new Color(fr, fg, fb); + areaLight.intensity = fint; + } + else + { + areaLight.color = ftraceLightColor.colorValue; + areaLight.intensity = ftraceLightIntensity.floatValue; + } + areaLight.bounceIntensity = ftraceLightIndirectIntensity.floatValue; + continue; + } + + var mats = mr.sharedMaterials; + if (mats.Length == 0 || mats[0] == null) continue; + + float intensity = ftraceLightIntensity.floatValue; + + var mat = mats[0]; + Undo.RecordObject(mat, "Change material"); + if (intensity > 1) + { + if (mat.shader.name != ftLightShaderName) mat.shader = Shader.Find(ftLightShaderName); + mat.color = ftraceLightColor.colorValue; + mat.mainTexture = ftraceLightTexture.objectReferenceValue as Texture2D; + mat.SetFloat("intensity", intensity); + } + else + { + if (ftraceLightTexture.objectReferenceValue == null) + { + if (mat.shader.name != ftLightShaderName && mat.shader.name != "Unlit/Color") mat.shader = Shader.Find(ftLightShaderName); + } + else + { + if (mat.shader.name != ftLightShaderName && mat.shader.name != "Unlit/Texture") mat.shader = Shader.Find(ftLightShaderName); + } + mat.mainTexture = ftraceLightTexture.objectReferenceValue as Texture2D; + if (mat.shader.name == ftLightShaderName) + { + mat.color = ftraceLightColor.colorValue; + mat.SetFloat("intensity", intensity); + } + else + { + mat.color = ftraceLightColor.colorValue * intensity; + } + } + } + } + } + + if (PlayerSettings.colorSpace == ColorSpace.Linear) + { + if (!GraphicsSettings.lightsUseLinearIntensity) + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Warning: project is not set up to use linear light intensity."); + EditorGUILayout.LabelField("GraphicsSettings.lightsUseLinearIntensity should be TRUE."); + if (GUILayout.Button("Fix")) + { + GraphicsSettings.lightsUseLinearIntensity = true; + } + } + else + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Project is using linear light intensity. This is nice."); + if (GUILayout.Button("Change to non-linear")) + { + GraphicsSettings.lightsUseLinearIntensity = false; + } + } + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightMeshInspector.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightMeshInspector.cs.meta new file mode 100644 index 00000000..d8581f93 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightMeshInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c2b4c5d630c305d44a44bc6a7fb96344 +timeCreated: 1525465024 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightingDataGen.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightingDataGen.cs new file mode 100644 index 00000000..8147679a --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightingDataGen.cs @@ -0,0 +1,202 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.IO; +using System.Collections.Generic; +using System.Runtime.InteropServices; +#if UNITY_EDITOR + +using UnityEngine.Rendering; +using System.Reflection; + +public class ftLightingDataGen +{ + // Generates LightingDataAsset for all lights with baked occlusionMaskChannel + public static bool GenerateShadowmaskLightingData(string outName, ref List lights, bool subtractive) + { + ftRenderLightmap.DebugLogInfo("Generating LightingDataAsset for " + lights.Count + " lights"); + + bool success = true; + try + { + PropertyInfo inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance); + var edPath = ftLightmaps.GetEditorPath(); +#if UNITY_2017_1_OR_NEWER + var bytesP0 = File.ReadAllBytes(edPath + "lightingDataChunks/LightingData_2017_1_part0.bin"); + var bytesP1 = File.ReadAllBytes(edPath + "lightingDataChunks/LightingData_2017_1_part1.bin"); + var bytesP2 = File.ReadAllBytes(edPath + "lightingDataChunks/LightingData_2017_1_part2.bin"); + var bytesP3 = File.ReadAllBytes(edPath + "lightingDataChunks/LightingData_2017_1_part3.bin"); +#else + var bytesP0 = File.ReadAllBytes(edPath + "lightingDataChunks/LightingData_5_6_part0.bin"); + var bytesP1 = File.ReadAllBytes(edPath + "lightingDataChunks/LightingData_5_6_part1.bin"); + var bytesP2 = File.ReadAllBytes(edPath + "lightingDataChunks/LightingData_5_6_part2.bin"); + var bytesP3 = File.ReadAllBytes(edPath + "lightingDataChunks/LightingData_5_6_part3.bin"); +#endif + var f = new BinaryWriter(File.Open(outName, FileMode.Create)); + f.Write(bytesP0); +#if UNITY_2017_1_OR_NEWER + f.Write(52 + 28 * lights.Count - 28); + f.Write(bytesP1); + f.Write(572 + 28 * lights.Count - 28); +#else + f.Write(160 + 28 * lights.Count - 28); + f.Write(bytesP1); + f.Write(552 + 28 * lights.Count - 28); +#endif + f.Write(bytesP2); + f.Write(lights.Count); + for(int i=0; i inID2OutID, ref Dictionary outIDChannel, bool subtractive) + { + try + { + var bytesIn = File.ReadAllBytes(inName); + + var lightCount = inID2OutID.Count; + if (lightCount == 0) return false; + + var inIDsAsBytes = new byte[lightCount][]; + var outIDsAsBytes = new byte[lightCount][]; + var outChannelsAsBytes = new byte[lightCount][]; + var matches = new int[lightCount]; + int counter = 0; + foreach(var pair in inID2OutID) + { + inIDsAsBytes[counter] = BitConverter.GetBytes(pair.Key); + outIDsAsBytes[counter] = BitConverter.GetBytes(pair.Value); + outChannelsAsBytes[counter] = BitConverter.GetBytes(outIDChannel[pair.Value]); + counter++; + } + + int replaced = 0; + int firstAddressReplaced = bytesIn.Length; + var lightsAsWritten = new int[lightCount]; + int lightsAsWrittenCounter = 0; + for(int i=0; i= 8 ? 0 : inIDsAsBytes[j][matches[j]]; + if (val == expectedVal) + { + matches[j]++; + if (matches[j] == 16) + { + // Matched long + 8 zeros + // Replace fileid + for(int k=0; k<8; k++) + { + //Debug.LogError("Matched " + inIDsAsBytes[j][k]+" "+outIDsAsBytes[j][k]); + bytesIn[i - 15 + k] = outIDsAsBytes[j][k]; + } + matches[j] = 0; + replaced++; + + int addr = i - 15; + if (addr < firstAddressReplaced) firstAddressReplaced = addr; + + lightsAsWritten[lightsAsWrittenCounter] = j; + lightsAsWrittenCounter++; + } + } + else + { + matches[j] = 0; + } + } + } + + if (firstAddressReplaced == bytesIn.Length) + { + ftRenderLightmap.DebugLogError("Failed to patch LightingDataAsset: unabled to replace light IDs"); + return false; + } + + if (lightsAsWrittenCounter != lightCount) + { + ftRenderLightmap.DebugLogError("Failed to patch LightingDataAsset: light count differs in temp/real scenes (" + lightsAsWrittenCounter + " vs " + lightCount + ")"); + return false; + } + + // IDs are patched. Now replace channels. + + for(int i=0; i(); + if (pstore == null) pstore = pstoreT.gameObject.AddComponent(); + return pstore; + } + + public override void OnInspectorGUI() { + + serializedObject.Update(); + var prev = isEnabled.boolValue; + EditorGUILayout.PropertyField(isEnabled, new GUIContent("Enable baking", "Prefab contents will be patched after baking if this checkbox is on. Patched prefab will be lightmapped when instantiated in any scene.")); + serializedObject.ApplyModifiedProperties(); + + if (isEnabled.boolValue != prev) + { + allPrefabsGood = true; + foreach(BakeryLightmappedPrefab selected in targets) + { + selected.enableBaking = isEnabled.boolValue; + Refresh(selected); + } + } + + if (allPrefabsGood) + { + EditorGUILayout.LabelField("Prefab connection: OK"); + } + else + { + foreach(BakeryLightmappedPrefab selected in targets) + { + if (selected.errorMessage.Length > 0) EditorGUILayout.LabelField("Error: " + selected.errorMessage); + } + } + + if (GUILayout.Button("Load render settings from prefab")) + { + if (EditorUtility.DisplayDialog("Bakery", "Change current render settings to prefab?", "OK", "Cancel")) + { + var storage = ftRenderLightmap.FindRenderSettingsStorage(); + foreach(BakeryLightmappedPrefab pref in targets) + { + var prefabStorage = FindPrefabStorage(pref); + ftLightmapsStorage.CopySettings(prefabStorage, storage); + } + var instance = (ftRenderLightmap)EditorWindow.GetWindow(typeof(ftRenderLightmap)); + if (instance != null) instance.LoadRenderSettings(); + } + } + + if (GUILayout.Button("Save current render settings to prefab")) + { + if (EditorUtility.DisplayDialog("Bakery", "Save current render settings to prefab?", "OK", "Cancel")) + { + var storage = ftRenderLightmap.FindRenderSettingsStorage(); + foreach(BakeryLightmappedPrefab pref in targets) + { + var prefabStorage = FindPrefabStorage(pref); + ftLightmapsStorage.CopySettings(storage, prefabStorage); + } + } + } + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmappedPrefabInspector.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmappedPrefabInspector.cs.meta new file mode 100644 index 00000000..2f55269d --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmappedPrefabInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a080b80faca4b9a4fa0a43361585c4be +timeCreated: 1541703652 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmapsStorageInspector.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmapsStorageInspector.cs new file mode 100644 index 00000000..8a5ef0a8 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmapsStorageInspector.cs @@ -0,0 +1,29 @@ + +using UnityEditor; +using UnityEngine; +using System; +using System.IO; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +[CustomEditor(typeof(ftLightmapsStorage))] +public class ftLightmapsStorageInspector : UnityEditor.Editor +{ + static bool showDebug = false; + + public override void OnInspectorGUI() { + + EditorGUILayout.LabelField("This object stores Bakery lightmapping data"); + + if (showDebug) + { + if (GUILayout.Button("Hide debug info")) showDebug = false; + DrawDefaultInspector(); + } + else + { + if (GUILayout.Button("Show debug info")) showDebug = true; + } + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmapsStorageInspector.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmapsStorageInspector.cs.meta new file mode 100644 index 00000000..a08b0777 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmapsStorageInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3ebd176ebc8a2304c84bc65c23bbecd6 +timeCreated: 1541939494 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftModelPostProcessor.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftModelPostProcessor.cs new file mode 100644 index 00000000..e4b165fe --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftModelPostProcessor.cs @@ -0,0 +1,418 @@ +using UnityEngine; +using UnityEditor; +using UnityEditor.SceneManagement; +using System.IO; +using System.Collections.Generic; + +public class ftModelPostProcessorInternal : AssetPostprocessor +{ + public virtual void UnwrapXatlas(Mesh m, UnwrapParam param) + { + } +} + +public partial class ftModelPostProcessor : ftModelPostProcessorInternal +{ + public static bool unwrapError = false; + public static string lastUnwrapErrorAsset = ""; + + // Deprecated but leave it for now just in case + public class ftSavedPadding : ScriptableObject + { + [SerializeField] + public ftGlobalStorage.AdjustedMesh data; + } + + static ftGlobalStorage storage; + UnwrapParam uparams; + const int res = 1024; + static Material mat; + public static RenderTexture rt; + public static Texture2D tex; + + static Dictionary assetHasPaddingAdjustment = new Dictionary(); + static Dictionary assetSavedPaddingAdjustment = new Dictionary(); + +#if UNITY_2017_1_OR_NEWER + bool deserializedSuccess = false; + ftGlobalStorage.AdjustedMesh deserialized; +#endif + + public static double GetTime() + { + return (System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond) / 1000.0; + } + + public static void Init() + { + storage = ftLightmaps.GetGlobalStorage(); + + //ftLightmaps.AddTag("BakeryProcessed"); + } + + void OnPreprocessModel() + { + Init(); + + assetHasPaddingAdjustment[assetPath] = false; + assetSavedPaddingAdjustment[assetPath] = null; + + ModelImporter importer = (ModelImporter)assetImporter; + + //if (storage == null) return; + bool hasGlobalPaddingAdjustment = (storage != null && storage.modifiedAssetPathList.IndexOf(assetPath) >= 0); + bool hasGlobalPaddingAdjustment2 = false; +#if UNITY_2017_1_OR_NEWER + var props = importer.extraUserProperties; + for(int p=0; p(json); + deserializedSuccess = true; + break; + } + } +#endif + if (storage != null) storage.InitModifiedMeshMap(assetPath); + + var tt = GetTime(); + AdjustUV(g.transform, saved); + Debug.Log("UV adjustment time: " + (GetTime() - tt)); + } + else + { + if (storage == null) return; + + Debug.Log("Bakery: checking for UV overlaps in " + assetPath); + + //if (g.tag == "BakeryProcessed") g.tag = ""; + ftLightmaps.MarkModelProcessed(assetPath, true);//false); + + // Manual UVs: check if overlapping + CheckUVOverlap(g, assetPath); + } + + if (g.tag == "BakeryProcessed") g.tag = ""; // remove legacy mark + } + + public static bool InitOverlapCheck() + { + rt = new RenderTexture(res, res, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); + tex = new Texture2D(res, res, TextureFormat.ARGB32, false, true); + var shdr = Shader.Find("Hidden/ftOverlapTest"); + if (shdr == null) + { + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + shdr = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftOverlapTest.shader", typeof(Shader)) as Shader; + if (shdr == null) + { + Debug.Log("No overlap testing shader present"); + return false; + } + } + mat = new Material(shdr); + return true; + } + + // -1 = No UVs + // 0 = no overlaps + // > 0 = overlapping pixels count + public static int DoOverlapCheck(GameObject g, bool deep) + { + int overlap = -1; + int overlapCounter = 0; + + Graphics.SetRenderTarget(rt); + GL.Clear(false, true, new Color(0,0,0,0)); + mat.SetPass(0); + + bool hasUV1 = RenderMeshes(g.transform, deep); + if (hasUV1) + { + tex.ReadPixels(new Rect(0,0,res,res), 0, 0, false); + tex.Apply(); + + var bytes = tex.GetRawTextureData(); + overlap = 0; + for(int i=0; i 1) + { + overlapCounter++; + if (overlapCounter > 256) // TODO: better check + { + overlap = 1; + break; + } + } + } + } + + Graphics.SetRenderTarget(null); + + return overlap == 1 ? overlapCounter : overlap; + } + + public static void EndOverlapCheck() + { + if (rt != null) rt.Release(); + if (tex != null) Object.DestroyImmediate(tex); + } + + public static void CheckUVOverlap(GameObject g, string assetPath) + { + bool canCheck = InitOverlapCheck(); + if (!canCheck) return; + + int overlap = DoOverlapCheck(g, true); + EndOverlapCheck(); + + if (overlap != 1 && overlap > 0) + { + Debug.LogWarning("[Bakery warning] " + overlap + " pixels overlap: " + assetPath); + } + + //var index = storage.assetList.IndexOf(assetPath); + var index = storage.assetList.IndexOf(assetPath); + var prevOverlap = -100; + if (index < 0) + { + //index = storage.assetList.Count; + //storage.assetList.Add(assetPath); + index = storage.assetList.Count; + storage.assetList.Add(assetPath); + storage.uvOverlapAssetList.Add(overlap); + } + else + { + prevOverlap = storage.uvOverlapAssetList[index]; + storage.assetList[index] = assetPath; + storage.uvOverlapAssetList[index] = overlap; + } + + if (prevOverlap != overlap) + { + EditorUtility.SetDirty(storage); + EditorSceneManager.MarkAllScenesDirty(); + } + } + + bool ValidateMesh(Mesh m, ftGlobalStorage.Unwrapper unwrapper) + { +#if UNITY_2017_3_OR_NEWER + #if UNITY_2018_4_OR_NEWER + // Bug was fixed in 2018.3.5, but the closest define is for 2018.4 + #else + if (m.indexFormat == UnityEngine.Rendering.IndexFormat.UInt32 && unwrapper == ftGlobalStorage.Unwrapper.Default) + { + Debug.LogError("Can't adjust UV padding for " + m.name + " due to Unity bug. Please set Index Format to 16-bit on the asset or use xatlas."); + return false; + } + #endif +#endif + return true; + } + + void AdjustUV(Transform t, ftSavedPadding2 saved = null) + { + var mf = t.GetComponent(); + if (mf != null && mf.sharedMesh != null) + { + var m = mf.sharedMesh; + var nm = m.name; + int modifiedMeshID; + + if (saved != null) + { + // Get padding from asset + int mindex = saved.data.meshName.IndexOf(nm); + if (mindex < 0) + { + //Debug.LogError("Unable to find padding value for mesh " + nm); + // This is fine. Apparently caused by parts of models being lightmapped, + // while other parts are not baked, yet still a part of the model. + } + else + { + var padding = saved.data.padding[mindex]; + + ftGlobalStorage.Unwrapper unwrapper = ftGlobalStorage.Unwrapper.Default; + if (saved.data.unwrapper != null && saved.data.unwrapper.Count > mindex) + unwrapper = (ftGlobalStorage.Unwrapper)saved.data.unwrapper[mindex]; + + if (!ValidateMesh(m, unwrapper)) return; + + uparams.packMargin = padding/1024.0f; + Unwrap(m, uparams, unwrapper); + } + } +#if UNITY_2017_1_OR_NEWER + else if (deserializedSuccess && deserialized.meshName != null && deserialized.padding != null) + { + // Get padding from extraUserProperties (new) + int mindex = deserialized.meshName.IndexOf(nm); + if (mindex < 0) + { + //Debug.LogError("Unable to find padding value for mesh " + nm); + // This is fine. Apparently caused by parts of models being lightmapped, + // while other parts are not baked, yet still a part of the model. + } + else + { + var padding = deserialized.padding[mindex]; + + ftGlobalStorage.Unwrapper unwrapper = ftGlobalStorage.Unwrapper.Default; + if (deserialized.unwrapper != null && deserialized.unwrapper.Count > mindex) + unwrapper = (ftGlobalStorage.Unwrapper)deserialized.unwrapper[mindex]; + + if (!ValidateMesh(m, unwrapper)) return; + + uparams.packMargin = padding/1024.0f; + Unwrap(m, uparams, unwrapper); + } + } + else + { + // Get padding from GlobalStorage (old) + if (storage != null && storage.modifiedMeshMap.TryGetValue(nm, out modifiedMeshID)) + { + var padding = storage.modifiedMeshPaddingArray[modifiedMeshID]; + + ftGlobalStorage.Unwrapper unwrapper = ftGlobalStorage.Unwrapper.Default; + if (storage.modifiedMeshUnwrapperArray != null && storage.modifiedMeshUnwrapperArray.Count > modifiedMeshID) + unwrapper = (ftGlobalStorage.Unwrapper)storage.modifiedMeshUnwrapperArray[modifiedMeshID]; + + if (!ValidateMesh(m, unwrapper)) return; + + uparams.packMargin = padding/1024.0f; + Unwrap(m, uparams, unwrapper); + } + } +#else + else if (storage != null && storage.modifiedMeshMap.TryGetValue(nm, out modifiedMeshID)) + { + var padding = storage.modifiedMeshPaddingArray[modifiedMeshID]; + + ftGlobalStorage.Unwrapper unwrapper = ftGlobalStorage.Unwrapper.Default; + if (storage.modifiedMeshUnwrapperArray != null && storage.modifiedMeshUnwrapperArray.Count > modifiedMeshID) + unwrapper = (ftGlobalStorage.Unwrapper)storage.modifiedMeshUnwrapperArray[modifiedMeshID]; + + if (!ValidateMesh(m, unwrapper)) return; + + uparams.packMargin = padding/1024.0f; + Unwrap(m, uparams, unwrapper); + } +#endif + } + + // Recurse + foreach (Transform child in t) + AdjustUV(child, saved); + } + + static bool RenderMeshes(Transform t, bool deep) + { + var mf = t.GetComponent(); + if (mf != null && mf.sharedMesh != null) + { + var m = mf.sharedMesh; + //var nm = m.name; + + bool noUV2 = (m.uv2 == null || (m.uv2.Length == 0 && m.vertexCount != 0)); + bool noUV1 = (m.uv == null || (m.uv.Length == 0 && m.vertexCount != 0)); + + if (noUV1 && noUV2) return false; + + mat.SetFloat("uvSet", noUV2 ? 0.0f : 1.0f); + mat.SetPass(0); + + Graphics.DrawMeshNow(m, Vector3.zero, Quaternion.identity); + } + + if (!deep) return true; + + // Recurse + foreach (Transform child in t) + RenderMeshes(child, deep); + + return true; + } + + void Unwrap(Mesh m, UnwrapParam uparams, ftGlobalStorage.Unwrapper unwrapper) + { + if (unwrapper == ftGlobalStorage.Unwrapper.xatlas) + { + UnwrapXatlas(m, uparams); + } + else + { + var tt = GetTime(); + Unwrapping.GenerateSecondaryUVSet(m, uparams); + if (m.uv2 == null || m.uv2.Length == 0) + { + Debug.LogError("Unity failed to unwrap mesh. Options: a) Use 32-bit indices and Unity >= 2018.4. b) Split it into multiple chunks. c) Disable 'Adjust UV Padding'."); + unwrapError = true; + lastUnwrapErrorAsset = assetPath; + } + Debug.Log("Unity unwrap time: " + (GetTime() - tt)); + } + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftModelPostProcessor.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftModelPostProcessor.cs.meta new file mode 100644 index 00000000..b4c82065 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftModelPostProcessor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 958d625f263bc9e468b7ea865c491cef +timeCreated: 1528661707 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftPointLightInspector.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftPointLightInspector.cs new file mode 100644 index 00000000..c1c65f41 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftPointLightInspector.cs @@ -0,0 +1,835 @@ + +using UnityEditor; +using UnityEngine; +using System; +using System.IO; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEngine.Rendering; + +[CustomEditor(typeof(BakeryPointLight))] +[CanEditMultipleObjects] +public class ftPointLightInspector : UnityEditor.Editor +{ + SerializedProperty ftraceLightColor; + SerializedProperty ftraceLightIntensity; + SerializedProperty ftraceLightShadowSpread; + SerializedProperty ftraceLightCutoff; + SerializedProperty ftraceLightSamples; + SerializedProperty ftraceLightProj; + SerializedProperty ftraceLightTexture; + SerializedProperty ftraceLightTexture2D; + SerializedProperty ftraceLightAngle; + SerializedProperty ftraceLightIES; + SerializedProperty ftraceLightBitmask; + SerializedProperty ftraceLightBakeToIndirect; + SerializedProperty ftraceLightRealisticFalloff; + SerializedProperty ftraceLightShadowmask; + SerializedProperty ftraceLightIndirectIntensity; + SerializedProperty ftraceLightFalloffMinRadius; + SerializedProperty ftraceLightInnerAngle; + SerializedProperty ftraceShadowmaskGroupID; + SerializedProperty ftraceDirectionMode; + + UnityEngine.Object spotCookieTexture; + + ftLightmapsStorage storage; + + bool isHDRP = false; + bool isLWRP = false; + + int projModeCached = -1; + int texCached = -1; + int tex2DCached = -1; + int iesCached = -1; + + static string[] selStrings = new string[] {"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16", + "17","18","19","20","21","22","23","24","25","26","27","28","29","30"};//,"31"}; + + void InitSerializedProperties(SerializedObject obj) + { + ftraceLightColor = obj.FindProperty("color"); + ftraceLightIntensity = obj.FindProperty("intensity"); + ftraceLightIndirectIntensity = obj.FindProperty("indirectIntensity"); + ftraceLightShadowSpread = obj.FindProperty("shadowSpread"); + ftraceLightCutoff = obj.FindProperty("cutoff"); + ftraceLightAngle = obj.FindProperty("angle"); + ftraceLightInnerAngle = obj.FindProperty("innerAngle"); + ftraceLightSamples = obj.FindProperty("samples"); + ftraceLightProj = obj.FindProperty("projMode"); + ftraceLightTexture = obj.FindProperty("cubemap"); + ftraceLightTexture2D = obj.FindProperty("cookie"); + ftraceLightIES = obj.FindProperty("iesFile"); + ftraceLightBitmask = obj.FindProperty("bitmask"); + ftraceLightBakeToIndirect = obj.FindProperty("bakeToIndirect"); + ftraceLightRealisticFalloff = obj.FindProperty("realisticFalloff"); + ftraceLightShadowmask = obj.FindProperty("shadowmask"); + ftraceLightFalloffMinRadius = obj.FindProperty("falloffMinRadius"); + ftraceShadowmaskGroupID = obj.FindProperty("shadowmaskGroupID"); + ftraceDirectionMode = obj.FindProperty("directionMode"); + + var hdrpLight = (target as BakeryPointLight).GetComponent("HDAdditionalLightData"); + isHDRP = hdrpLight != null; + +#if UNITY_2018_1_OR_NEWER + +#if UNITY_2019_3_OR_NEWER + var rpipe = GraphicsSettings.currentRenderPipeline; +#else + var rpipe = GraphicsSettings.renderPipelineAsset; +#endif + + if (rpipe != null && (rpipe.GetType().Name.StartsWith("Lightweight") || rpipe.GetType().Name.StartsWith("Universal"))) + { + isLWRP = true; + } +#endif + } + + void OnEnable() + { + InitSerializedProperties(serializedObject); + + if (spotCookieTexture == null) + { + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + spotCookieTexture = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftUnitySpotTexture.bmp", typeof(Texture2D)); + } + } + + void GetLinearLightParameters(Light light, out float lightR, out float lightG, out float lightB, out float lightInt) + { + if (PlayerSettings.colorSpace != ColorSpace.Linear) + { + lightInt = light.intensity; + lightR = light.color.r; + lightG = light.color.g; + lightB = light.color.b; + return; + } + + if (!GraphicsSettings.lightsUseLinearIntensity) + { + lightR = Mathf.Pow(light.color.r * light.intensity, 2.2f); + lightG = Mathf.Pow(light.color.g * light.intensity, 2.2f); + lightB = Mathf.Pow(light.color.b * light.intensity, 2.2f); + lightInt = Mathf.Max(Mathf.Max(lightR, lightG), lightB); + lightR /= lightInt; + lightG /= lightInt; + lightB /= lightInt; + } + else + { + lightInt = light.intensity; + lightR = light.color.linear.r; + lightG = light.color.linear.g; + lightB = light.color.linear.b; + } + } + + bool CompareWithLWRP(Light l, ref string why) + { + if (l.type == LightType.Spot) + { + var so = new SerializedObject(l); + if (so == null) + { + why = "no SerializedObject"; + return false; + } + if (ftraceLightProj.intValue != (int)BakeryPointLight.ftLightProjectionMode.Cone) + { + why = "spot shape doesn't match."; + return false; + } + SerializedProperty innerAngle = so.FindProperty("m_InnerSpotAngle"); + if (innerAngle == null) + { + why = "no m_InnerSpotAngle"; + return false; + } + if (Mathf.Abs(((ftraceLightInnerAngle.floatValue * 0.01f) * ftraceLightAngle.floatValue) - innerAngle.floatValue) > 0.001f) + { + why = "inner angle doesn't match."; + return false; + } + } + return true; + } + + bool CompareWithHDRP(Light l, ref string why) + { + var hdrpLight = l.GetComponent("HDAdditionalLightData"); + if (hdrpLight == null) + { + why = "no HDAdditionalLightData"; + return false; + } + var so = new SerializedObject(hdrpLight); + if (so == null) + { + why = "no SerializedObject"; + return false; + } + SerializedProperty hdrpLightTypeExtent = so.FindProperty("m_PointlightHDType"); + if (hdrpLightTypeExtent == null) + { + why = "no m_PointlightHDType"; + return false; + } + + int extendedLightType = hdrpLightTypeExtent.intValue; + if (extendedLightType != 0) + { + why = "Only punctual sounrces are supported.\nUse rectangle/tube geometry with Light Mesh instead."; + return false; + } + + if (l.type == LightType.Spot) + { + SerializedProperty hdrpLightSpotShape = so.FindProperty("m_SpotLightShape"); + if (hdrpLightSpotShape == null) + { + why = "no m_SpotLightShape"; + return false; + } + SerializedProperty hdrpLightInnerAngle = so.FindProperty("m_InnerSpotPercent"); + if (hdrpLightInnerAngle == null) + { + why = "no m_InnerSpotPercent"; + return false; + } + + int spotShape = hdrpLightSpotShape.intValue; + if (spotShape != 0) + { + why = "Only cone spotlights are supported."; + return false; + } + if (ftraceLightProj.intValue != (int)BakeryPointLight.ftLightProjectionMode.Cone) + { + why = "spot shape doesn't match."; + return false; + } + if (Mathf.Abs(ftraceLightInnerAngle.floatValue - hdrpLightInnerAngle.floatValue) > 0.001f) + { + why = "inner angle doesn't match."; + return false; + } + } + + return true; + } + + void MatchToLWRPLight(Light l) + { + ftraceLightRealisticFalloff.boolValue = true; + ftraceLightFalloffMinRadius.floatValue = 0.01f; + if (l.type == LightType.Spot) + { + ftraceLightProj.intValue = (int)BakeryPointLight.ftLightProjectionMode.Cone; + + var so = new SerializedObject(l); + if (so == null) return; + + SerializedProperty lightInnerAngle = so.FindProperty("m_InnerSpotAngle"); + if (lightInnerAngle == null) return; + ftraceLightInnerAngle.floatValue = (lightInnerAngle.floatValue / ftraceLightAngle.floatValue) * 100; + } + } + + void MatchToHDRPLight(Light l) + { + ftraceLightRealisticFalloff.boolValue = true; + ftraceLightFalloffMinRadius.floatValue = 0.01f; + + ftraceLightIntensity.floatValue /= Mathf.PI; + + var hdrpLight = l.GetComponent("HDAdditionalLightData"); + if (hdrpLight == null) return; + + var so = new SerializedObject(hdrpLight); + if (so == null) return; + + SerializedProperty hdrpLightTypeExtent = so.FindProperty("m_PointlightHDType"); + if (hdrpLightTypeExtent == null) return; + + int extendedLightType = hdrpLightTypeExtent.intValue; + if (extendedLightType != 0) return; + + if (l.type == LightType.Spot) + { + SerializedProperty hdrpLightSpotShape = so.FindProperty("m_SpotLightShape"); + if (hdrpLightSpotShape == null) return; + + int spotShape = hdrpLightSpotShape.intValue; + if (spotShape != 0) return; + + ftraceLightProj.intValue = (int)BakeryPointLight.ftLightProjectionMode.Cone; + } + + SerializedProperty hdrpLightInnerAngle = so.FindProperty("m_InnerSpotPercent"); + if (hdrpLightInnerAngle == null) return; + ftraceLightInnerAngle.floatValue = hdrpLightInnerAngle.floatValue; + } + + void SetLWRPLight(Light l) + { + if (ftraceLightProj.enumValueIndex == (int)BakeryPointLight.ftLightProjectionMode.Cone) + { + var so = new SerializedObject(l); + if (so == null) return; + + SerializedProperty lightInnerAngle = so.FindProperty("m_InnerSpotAngle"); + if (lightInnerAngle == null) return; + + lightInnerAngle.floatValue = (ftraceLightInnerAngle.floatValue * 0.01f) * ftraceLightAngle.floatValue; + + so.ApplyModifiedProperties(); + } + } + + void SetHDRPLight(Light l) + { +#if UNITY_2019_1_OR_NEWER + l.useBoundingSphereOverride = false; + l.useShadowMatrixOverride = false; +#endif + l.intensity *= Mathf.PI; + + var hdrpLight = l.GetComponent("HDAdditionalLightData"); + if (hdrpLight == null) return; + + var so = new SerializedObject(hdrpLight); + if (so == null) return; + + SerializedProperty hdrpUnits = so.FindProperty("m_LightUnit"); + if (hdrpUnits != null) hdrpUnits.intValue = 1; // candela + + SerializedProperty hdrpInt2 = so.FindProperty("m_Intensity"); + if (hdrpInt2 != null) hdrpInt2.floatValue = l.intensity; + + SerializedProperty hdrpLightTypeExtent = so.FindProperty("m_PointlightHDType"); + if (hdrpLightTypeExtent == null) return; + hdrpLightTypeExtent.intValue = 0; // punctual + + if (ftraceLightProj.enumValueIndex == (int)BakeryPointLight.ftLightProjectionMode.Cone) + { + SerializedProperty hdrpLightSpotShape = so.FindProperty("m_SpotLightShape"); + if (hdrpLightSpotShape == null) return; + hdrpLightSpotShape.intValue = 0; // cone + } + + SerializedProperty hdrpLightInnerAngle = so.FindProperty("m_InnerSpotPercent"); + if (hdrpLightInnerAngle == null) return; + hdrpLightInnerAngle.floatValue = ftraceLightInnerAngle.floatValue; + + so.ApplyModifiedProperties(); + } + + void TestPreviewRefreshProperty(ref int cached, int newVal) + { + if (cached >= 0) + { + if (cached != newVal) + { + BakeryPointLight.lightsChanged = 2; + } + } + cached = newVal; + } + + void TestPreviewRefreshProperty(ref int cached, UnityEngine.Object newVal) + { + if (newVal == null) + { + TestPreviewRefreshProperty(ref cached, 0); + return; + } + TestPreviewRefreshProperty(ref cached, newVal.GetInstanceID()); + } + + public override void OnInspectorGUI() { + //if (showFtrace) + { + OnEnable(); + + serializedObject.Update(); + + TestPreviewRefreshProperty(ref projModeCached, ftraceLightProj.intValue); + TestPreviewRefreshProperty(ref texCached, ftraceLightTexture.objectReferenceValue); + TestPreviewRefreshProperty(ref tex2DCached, ftraceLightTexture2D.objectReferenceValue); + TestPreviewRefreshProperty(ref iesCached, ftraceLightIES.objectReferenceValue); + + EditorGUILayout.PropertyField(ftraceLightColor, new GUIContent("Color", "Color of the light")); + EditorGUILayout.PropertyField(ftraceLightIntensity, new GUIContent("Intensity", "Color multiplier (Candela / PI)")); + EditorGUILayout.PropertyField(ftraceLightShadowSpread, new GUIContent("Shadow spread", "Controls shadow blurriness from 0 to 1")); + + EditorGUILayout.PropertyField(ftraceLightRealisticFalloff, new GUIContent("Physical falloff", "Use inverse-squared falloff instead of Unity falloff")); + if (ftraceLightRealisticFalloff.boolValue) + { + EditorGUILayout.PropertyField(ftraceLightFalloffMinRadius, new GUIContent("Falloff min size", "As point lights don't have area, at zero distance 1/(d*d) will become infinity. This value avoids this issue by assuming the light to have some minimum radius and changing the formula to 1/(d*d+R*R).")); + } + + EditorGUILayout.PropertyField(ftraceLightCutoff, new GUIContent("Range", "Lighting distance limit. When 'Physical falloff' is on, for maximum corectness set to a very high value. Using smaller values is useful for faster render times and to match real-time lights. Bakery uses Skyforge falloff to maintain balance between correct inverse-squared attenuation and practical limits (https://habr.com/company/mailru/blog/248873/)")); + EditorGUILayout.PropertyField(ftraceLightSamples, new GUIContent("Samples", "The amount of sample points generated on the surface of this light. Point light shadows are traced towards points on a sphere (with radius = shadowSpread) around the light. ")); + EditorGUILayout.PropertyField(ftraceLightProj, new GUIContent("Projection mask", "Determines additional light masking mode.")); + + switch(ftraceLightProj.enumValueIndex) + { + case (int)BakeryPointLight.ftLightProjectionMode.Cookie: + EditorGUILayout.PropertyField(ftraceLightTexture2D, new GUIContent("Cookie texture", "Texture")); + EditorGUILayout.Slider(ftraceLightAngle, 1, 179, new GUIContent("Angle", "Angle of projection (like in spotlights).")); + break; + case (int)BakeryPointLight.ftLightProjectionMode.Cone: + EditorGUILayout.Slider(ftraceLightAngle, 1, 180, new GUIContent("Outer angle")); + EditorGUILayout.Slider(ftraceLightInnerAngle, 0, 100, new GUIContent("Inner angle percent")); + break; + case (int)BakeryPointLight.ftLightProjectionMode.Cubemap: + EditorGUILayout.PropertyField(ftraceLightTexture, new GUIContent("Projected cubemap", "Cubemap")); + break; + case (int)BakeryPointLight.ftLightProjectionMode.IES: + ftraceLightIES.objectReferenceValue = EditorGUILayout.ObjectField("IES file", ftraceLightIES.objectReferenceValue, typeof(UnityEngine.Object), false); + if (ftraceLightIES.objectReferenceValue != null) + { + var path = AssetDatabase.GetAssetPath(ftraceLightIES.objectReferenceValue); + if (path.Length < 4 || path.Substring(path.Length - 4).ToLower() != ".ies") + { + EditorUtility.DisplayDialog("Bakery", "File must have IES extension.", "OK"); + ftraceLightIES.objectReferenceValue = null; + } + } + EditorGUILayout.PropertyField(ftraceDirectionMode, new GUIContent("Orientation", "Defines forward axis for the IES light.")); + break; + default: + break; + } + + int prevVal = ftraceLightBitmask.intValue; + int newVal = EditorGUILayout.MaskField(new GUIContent("Bitmask", "Lights only affect renderers with overlapping bits"), ftraceLightBitmask.intValue, selStrings); + if (prevVal != newVal) ftraceLightBitmask.intValue = newVal; + + /* + EditorGUILayout.PropertyField(ftraceLightBakeToIndirect, new GUIContent("Bake to indirect", "Add direct contribution from this light to indirect-only lightmaps")); + if (ftraceLightBakeToIndirect.boolValue && ftraceLightShadowmask.boolValue) ftraceLightShadowmask.boolValue = false; + + EditorGUILayout.PropertyField(ftraceLightShadowmask, new GUIContent("Shadowmask", "Enable mixed lighting. Static shadows from this light will be baked, and real-time light will cast shadows from dynamic objects.")); + if (ftraceLightBakeToIndirect.boolValue && ftraceLightShadowmask.boolValue) ftraceLightBakeToIndirect.boolValue = false; + */ + + if (storage == null) storage = ftRenderLightmap.FindRenderSettingsStorage(); + var rmode = storage.renderSettingsUserRenderMode; + if (rmode != (int)ftRenderLightmap.RenderMode.FullLighting) + { + ftDirectLightInspector.BakeWhat contrib; + if (ftraceLightShadowmask.boolValue) + { + contrib = ftDirectLightInspector.BakeWhat.IndirectAndShadowmask; + } + else if (ftraceLightBakeToIndirect.boolValue) + { + contrib = ftDirectLightInspector.BakeWhat.DirectAndIndirect; + } + else + { + contrib = ftDirectLightInspector.BakeWhat.IndirectOnly; + } + var prevContrib = contrib; + + if (rmode == (int)ftRenderLightmap.RenderMode.Indirect) + { + contrib = (ftDirectLightInspector.BakeWhat)EditorGUILayout.Popup("Baked contribution", (int)contrib, ftDirectLightInspector.directContributionIndirectOptions); + } + else if (rmode == (int)ftRenderLightmap.RenderMode.Shadowmask) + { + contrib = (ftDirectLightInspector.BakeWhat)EditorGUILayout.EnumPopup("Baked contribution", contrib); + } + + if (prevContrib != contrib) + { + if (contrib == ftDirectLightInspector.BakeWhat.IndirectOnly) + { + ftraceLightShadowmask.boolValue = false; + ftraceLightBakeToIndirect.boolValue = false; + } + else if (contrib == ftDirectLightInspector.BakeWhat.IndirectAndShadowmask) + { + ftraceLightShadowmask.boolValue = true; + ftraceLightBakeToIndirect.boolValue = false; + } + else + { + ftraceLightShadowmask.boolValue = false; + ftraceLightBakeToIndirect.boolValue = true; + } + } + } + + EditorGUILayout.PropertyField(ftraceLightIndirectIntensity, new GUIContent("Indirect intensity", "Non-physical GI multiplier for this light")); + + if (ftraceLightShadowmask.boolValue) + { + EditorGUILayout.PropertyField(ftraceShadowmaskGroupID, new GUIContent("Shadowmask Group ID", "If set to 0, each shadowmasked light will have a separate mask. Lights sharing any other positive value will share the same mask. This is useful to avoid 4 channel limit in cases where light bounds overlap, but the overlapping part is occluded in both anyway.")); + } + + serializedObject.ApplyModifiedProperties(); + } + + + bool showWarningCant = false; + bool showError = false; + string why = ""; + + bool shadowmaskNoDynamicLight = false; + + foreach(BakeryPointLight selectedLight in targets) + { + bool match = true; + //string why = ""; + var light = selectedLight.GetComponent(); + if (light == null) + { + if (ftraceLightShadowmask.boolValue) shadowmaskNoDynamicLight = true; + continue; + } + if (!light.enabled) + { + if (ftraceLightShadowmask.boolValue) shadowmaskNoDynamicLight = true; + } + + if (isHDRP) + { + if (!ftraceLightRealisticFalloff.boolValue || Mathf.Abs(ftraceLightFalloffMinRadius.floatValue - 0.01f) > 0.0001f) + { + match = false; + why = "falloff doesn't match HDRP"; + } + else + { + match = CompareWithHDRP(light, ref why); + } + } + + if (isLWRP) + { + if (!ftraceLightRealisticFalloff.boolValue || Mathf.Abs(ftraceLightFalloffMinRadius.floatValue - 0.01f) > 0.0001f) + { + match = false; + why = "falloff doesn't match URP"; + } + else + { + match = CompareWithLWRP(light, ref why); + } + } + + var so = new SerializedObject(selectedLight); + InitSerializedProperties(so); + + if (ftraceLightIndirectIntensity.floatValue != light.bounceIntensity) + { + match = false; + why = "indirect intensity doesn't match"; + } + + if (ftraceLightProj.enumValueIndex == (int)BakeryPointLight.ftLightProjectionMode.IES) + { + showWarningCant = true; + } + else if (ftraceLightProj.enumValueIndex == (int)BakeryPointLight.ftLightProjectionMode.Omni) + { + if (light.type != LightType.Point) + { + match = false; + why = "real-time light is not point"; + } + else if (light.cookie != null) + { + match = false; + why = "real-time light has cookie"; + } + } + else if (ftraceLightProj.enumValueIndex == (int)BakeryPointLight.ftLightProjectionMode.Cubemap) + { + if (light.type != LightType.Point) + { + match = false; + why = "real-time light is not point"; + } + else if (light.cookie == null) + { + match = false; + why = "real-time light has no cookie"; + } + } + else if (ftraceLightProj.enumValueIndex == (int)BakeryPointLight.ftLightProjectionMode.Cookie) + { + if (light.type != LightType.Spot) + { + match = false; + why = "real-time light is not spot"; + } + else if (light.cookie == null && ftraceLightTexture2D.objectReferenceValue != spotCookieTexture) + { + match = false; + why = "wrong cookie texture"; + } + else if (light.cookie != null && ftraceLightTexture2D.objectReferenceValue != light.cookie) + { + match = false; + why = "wrong cookie texture"; + } + else if (light.spotAngle != ftraceLightAngle.floatValue) + { + match = false; + why = "spot angle doesn't match"; + } + } + else if (ftraceLightProj.enumValueIndex == (int)BakeryPointLight.ftLightProjectionMode.Cone) + { + if (light.type != LightType.Spot) + { + match = false; + why = "real-time light is not spot"; + } + else if (light.spotAngle != ftraceLightAngle.floatValue) + { + match = false; + why = "outer angle doesn't match"; + } + } + + var clr = ftraceLightColor.colorValue; + float eps = 1.0f / 255.0f; + float lightR, lightG, lightB, lightInt; + float fr, fg, fb; + float fintensity = ftraceLightIntensity.floatValue; + if (isHDRP) fintensity *= Mathf.PI; + if (PlayerSettings.colorSpace == ColorSpace.Linear) + { + fr = clr.linear.r;// * fintensity; + fg = clr.linear.g;// * fintensity; + fb = clr.linear.b;// * fintensity; + } + else + { + fr = clr.r; + fg = clr.g; + fb = clr.b; + } + GetLinearLightParameters(light, out lightR, out lightG, out lightB, out lightInt); + + if (GraphicsSettings.lightsUseLinearIntensity || PlayerSettings.colorSpace != ColorSpace.Linear) + { + if (Mathf.Abs(lightR - fr) > eps || Mathf.Abs(lightG - fg) > eps || Mathf.Abs(lightB - fb) > eps) + { + match = false; + why = "color doesn't match"; + } + else if (Mathf.Abs(lightInt - fintensity) > eps) + { + match = false; + why = "intensity doesn't match"; + } + } + else + { + eps *= Mathf.Max(lightInt, fintensity); + if (Mathf.Abs(lightR*lightInt - fr*fintensity) > eps || + Mathf.Abs(lightG*lightInt - fg*fintensity) > eps || + Mathf.Abs(lightB*lightInt - fb*fintensity) > eps) + { + match = false; + why = "intensity doesn't match"; + } + } + + if (Mathf.Abs(light.range - ftraceLightCutoff.floatValue) > 0.001f) + { + match = false; + why = "range doesn't match"; + } + + if (!match) + { + showError = true; + } + } + + if (shadowmaskNoDynamicLight) + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Warning: shadowmask needs enabled real-time light to work"); + } + + if (showWarningCant) + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Warning: real-time light can't match baked IES light"); + EditorGUILayout.Space(); + } + + if (showError) + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Real-time light doesn't match lightmap: " + why); + + if (GUILayout.Button("Match lightmapped to real-time")) + { + foreach(BakeryPointLight selectedLight in targets) + { + var light = selectedLight.GetComponent(); + if (light == null) continue; + //if (!light.enabled) continue; + var so = new SerializedObject(selectedLight); + InitSerializedProperties(so); + + if (PlayerSettings.colorSpace != ColorSpace.Linear) + { + ftraceLightColor.colorValue = light.color; + ftraceLightIntensity.floatValue = light.intensity; + } + else if (!GraphicsSettings.lightsUseLinearIntensity) + { + float lightR, lightG, lightB, lightInt; + GetLinearLightParameters(light, out lightR, out lightG, out lightB, out lightInt); + ftraceLightColor.colorValue = new Color(lightR, lightG, lightB); + ftraceLightIntensity.floatValue = lightInt; + } + else + { + ftraceLightColor.colorValue = light.color; + ftraceLightIntensity.floatValue = light.intensity; + } + ftraceLightCutoff.floatValue = light.range; + ftraceLightAngle.floatValue = light.spotAngle; + + if (light.type == LightType.Point) + { + if (light.cookie == null) + { + ftraceLightProj.enumValueIndex = (int)BakeryPointLight.ftLightProjectionMode.Omni; + ftraceLightTexture.objectReferenceValue = null; + } + else + { + ftraceLightProj.enumValueIndex = (int)BakeryPointLight.ftLightProjectionMode.Cubemap; + ftraceLightTexture.objectReferenceValue = light.cookie; + } + } + else if (light.type == LightType.Spot) + { + ftraceLightProj.enumValueIndex = (int)BakeryPointLight.ftLightProjectionMode.Cookie; + if (light.cookie == null) + { + ftraceLightTexture2D.objectReferenceValue = spotCookieTexture; + } + else + { + ftraceLightTexture2D.objectReferenceValue = light.cookie; + } + } + ftraceLightIndirectIntensity.floatValue = light.bounceIntensity; + + if (isHDRP) MatchToHDRPLight(light); + if (isLWRP) MatchToLWRPLight(light); + + so.ApplyModifiedProperties(); + } + } + if (GUILayout.Button("Match real-time to lightmapped")) + { + foreach(BakeryPointLight selectedLight in targets) + { + var light = selectedLight.GetComponent(); + if (light == null) continue; + //if (!light.enabled) continue; + var so = new SerializedObject(selectedLight); + InitSerializedProperties(so); + + Undo.RecordObject(light, "Change light"); + if (PlayerSettings.colorSpace != ColorSpace.Linear) + { + light.color = ftraceLightColor.colorValue; + light.intensity = ftraceLightIntensity.floatValue; + } + else if (!GraphicsSettings.lightsUseLinearIntensity) + { + var clr = ftraceLightColor.colorValue; + float fintensity = ftraceLightIntensity.floatValue; + float fr = clr.linear.r;// * fintensity; + float fg = clr.linear.g;// * fintensity; + float fb = clr.linear.b;// * fintensity; + + fr = Mathf.Pow(fr * fintensity, 1.0f / 2.2f); + fg = Mathf.Pow(fg * fintensity, 1.0f / 2.2f); + fb = Mathf.Pow(fb * fintensity, 1.0f / 2.2f); + float fint = Mathf.Max(Mathf.Max(fr, fg), fb); + fr /= fint; + fg /= fint; + fb /= fint; + light.color = new Color(fr, fg, fb); + light.intensity = fint; + } + else + { + light.color = ftraceLightColor.colorValue; + light.intensity = ftraceLightIntensity.floatValue; + } + light.range = ftraceLightCutoff.floatValue; + light.spotAngle = ftraceLightAngle.floatValue; + + if (ftraceLightProj.enumValueIndex == (int)BakeryPointLight.ftLightProjectionMode.Omni) + { + light.type = LightType.Point; + light.cookie = null; + } + else if (ftraceLightProj.enumValueIndex == (int)BakeryPointLight.ftLightProjectionMode.Cubemap) + { + light.type = LightType.Point; + light.cookie = ftraceLightTexture.objectReferenceValue as Cubemap; + } + else if (ftraceLightProj.enumValueIndex == (int)BakeryPointLight.ftLightProjectionMode.Cookie) + { + light.type = LightType.Spot; + light.cookie = ftraceLightTexture.objectReferenceValue == spotCookieTexture ? null : (ftraceLightTexture.objectReferenceValue as Texture2D); + } + else if (ftraceLightProj.enumValueIndex == (int)BakeryPointLight.ftLightProjectionMode.Cone) + { + light.type = LightType.Spot; + } + light.bounceIntensity = ftraceLightIndirectIntensity.floatValue; + + if (isHDRP) SetHDRPLight(light); + if (isLWRP) SetLWRPLight(light); + } + } + } + + if (PlayerSettings.colorSpace == ColorSpace.Linear) + { + if (!GraphicsSettings.lightsUseLinearIntensity) + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Warning: project is not set up to use linear light intensity."); + EditorGUILayout.LabelField("GraphicsSettings.lightsUseLinearIntensity should be TRUE."); + if (GUILayout.Button("Fix")) + { + GraphicsSettings.lightsUseLinearIntensity = true; + } + } + else + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Project is using linear light intensity. This is nice."); + if (GUILayout.Button("Change to non-linear")) + { + GraphicsSettings.lightsUseLinearIntensity = false; + } + } + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftPointLightInspector.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftPointLightInspector.cs.meta new file mode 100644 index 00000000..b79d8caa --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftPointLightInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d965ed7d9a9a406418fe8b269b3fef30 +timeCreated: 1525513538 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRenderLightmap.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRenderLightmap.cs new file mode 100644 index 00000000..dcb9e1b8 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRenderLightmap.cs @@ -0,0 +1,10681 @@ +#if UNITY_EDITOR + +// Disable 'obsolete' warnings +#pragma warning disable 0618 + +// Run Bakery exes via CreateProcess instead of mono. Mono seems to have problems with apostrophes in paths. +// Bonus point: working dir == DLL dir, so moving the folder works. +#define LAUNCH_VIA_DLL +//#define COMPRESS_VOLUME_RGBM + +using UnityEngine; +using UnityEngine.Rendering; +using UnityEditor; +using System.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEditor.SceneManagement; +using UnityEngine.SceneManagement; +using System.Text.RegularExpressions; +using System.Reflection; + +public class ftRenderLightmap : EditorWindow//ScriptableWizard +{ + public static bool ftInitialized = false; + public static bool ftSceneDirty = true; + + public static ftRenderLightmap instance; + + public enum RenderMode + { + FullLighting = 0, + Indirect = 1, + Shadowmask = 2, + Subtractive = 3, + AmbientOcclusionOnly = 4 + }; + + public enum RenderDirMode + { + None = 0, + BakedNormalMaps = 1, + DominantDirection = 2, + RNM = 3, + SH = 4 + }; + + public enum SettingsMode + { + Simple = 0, + Advanced = 1, + Experimental = 2 + }; + + public enum LightProbeMode + { + Legacy = 0, + L1 = 1 + }; + + public enum GILODMode + { + Auto = 0, + ForceOn = 1, + ForceOff = 2 + }; + + class Convex + { + public Vector3[] vertices; + public Plane[] planes; + }; + + [DllImport ("simpleProgressBar", CallingConvention=CallingConvention.Cdecl)] + public static extern int simpleProgressBarShow(string header, string msg, float percent, float step, bool onTop); + + [DllImport ("simpleProgressBar", CallingConvention=CallingConvention.Cdecl)] + public static extern bool simpleProgressBarCancelled(); + + [DllImport ("simpleProgressBar", CallingConvention=CallingConvention.Cdecl)] + public static extern void simpleProgressBarEnd(); + + [DllImport ("lmrebake", CallingConvention=CallingConvention.Cdecl)] + public static extern int lmrInit(System.IntPtr device); + + [DllImport ("lmrebake", CallingConvention=CallingConvention.Cdecl)] + public static extern int lmrRender(string srcLMFilename, string destLMFilename, string lodMaskFilename, + float[] srcUV, float[] destUV, int floatOffset, int numFloats, int[] indices, int indexOffset, int numIndices, + int destWidth, int destHeight, int lodBits); + + [DllImport ("lmrebake", CallingConvention=CallingConvention.Cdecl)] + public static extern int lmrRenderSimple(string srcLMFilename, string destLMFilename, + int destWidth, int destHeight, int lodBits); + + [DllImport ("halffloat2vb", CallingConvention=CallingConvention.Cdecl)] + public static extern int halffloat2vb([MarshalAs(UnmanagedType.LPWStr)]string inputFilename, System.IntPtr values, int dataType); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern System.IntPtr RunLocalProcess([MarshalAs(UnmanagedType.LPWStr)]string commandline, bool setWorkDir); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern System.IntPtr RunLocalProcessVisible([MarshalAs(UnmanagedType.LPWStr)]string commandline); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern bool IsProcessFinished(System.IntPtr proc); + + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern int GetProcessReturnValueAndClose(System.IntPtr proc); + +#if UNITY_2018_3_OR_NEWER + [DllImport("user32.dll")] + static extern System.IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + static extern System.IntPtr GetParent(System.IntPtr hwnd); + + [DllImport("user32.dll")] + static extern int GetWindowText(System.IntPtr hwnd, StringBuilder text, int count); + + System.IntPtr unityEditorHWND; +#endif + +#if UNITY_2017_3_OR_NEWER + const LightmapEditorSettings.Lightmapper BUILTIN_RADIOSITY = LightmapEditorSettings.Lightmapper.Enlighten; + const LightmapEditorSettings.Lightmapper BUILTIN_PT = LightmapEditorSettings.Lightmapper.ProgressiveCPU; +#else + #if UNITY_2017_2_OR_NEWER + const LightmapEditorSettings.Lightmapper BUILTIN_RADIOSITY = LightmapEditorSettings.Lightmapper.Radiosity; + const LightmapEditorSettings.Lightmapper BUILTIN_PT = LightmapEditorSettings.Lightmapper.PathTracer; + #endif +#endif + + public static int bounces = 5; + public int giSamples = 16; + static public float giBackFaceWeight = 0; + public static int tileSize = 512; + public float priority = 2; + public float texelsPerUnit = 20; + public static bool forceRefresh = true; + bool forceRebuildGeometry = true; + bool performRendering = true; + public RenderMode userRenderMode = RenderMode.FullLighting; + public static bool isDistanceShadowmask; + public static RenderDirMode renderDirMode; + public static LightProbeMode lightProbeMode = LightProbeMode.L1; + public static ftGlobalStorage.Unwrapper unwrapper = ftGlobalStorage.Unwrapper.Default; + public static ftGlobalStorage.DenoiserType denoiserType = ftGlobalStorage.DenoiserType.OpenImageDenoise; + public SettingsMode settingsMode = SettingsMode.Simple; + public static GILODMode giLodMode = GILODMode.ForceOff; + public static bool giLodModeEnabled = true; + static bool revertReflProbesValue = false; + static bool reflProbesValue = true; + public static bool restoreFromGlobalSector = false; + public static float hackEmissiveBoost = 1; + public static float hackIndirectBoost = 1; + public static float hackAOIntensity = 0; + public static int hackAOSamples = 16; + public static float hackAORadius = 1; + public static bool showAOSettings = false; + public static bool showTasks = false; + public static bool showTasks2 = false; + public static bool showPaths = false; + public static bool showNet = false; + public static bool showPerf = true; + //public static bool showCompression = false; + //public static bool useUnityForLightProbes = false; + public static bool useUnityForOcclsusionProbes = false; + public static bool showDirWarning = true; + public static bool showCheckerSettings = false; + public static bool showChecker = false; + static bool usesRealtimeGI = false; + static int lastBakeTime; + public static bool beepOnFinish; + public static bool useScenePath = true; + //public static TextureImporterFormat lightmapCompressionColor = TextureImporterFormat.Automatic; + //public static TextureImporterFormat lightmapCompressionMask = TextureImporterFormat.Automatic; + //public static TextureImporterFormat lightmapCompressionDir = TextureImporterFormat.Automatic; + public static bool removeDuplicateLightmaps = false; + public static bool clientMode = false; + public static int sampleDivisor = 1; + + public bool exeMode = true;//false; + public bool deferredMode = true; // defer calls to ftrace and denoiser to unload unity scenes + public bool unloadScenesInDeferredMode = false; + public static bool adjustSamples = true; + public static bool checkOverlaps = false; + public static bool samplesWarning = true; + public static bool prefabWarning = true; + public static bool compressedGBuffer = true; + public static bool compressedOutput = true; + static List deferredCommands; + static Dictionary> deferredCommandsFallback; + static Dictionary deferredCommandsRebake; + static Dictionary deferredCommandsLODGen; + static Dictionary deferredCommandsGIGen; + static Dictionary deferredCommandsHalf2VB; + static Dictionary deferredCommandsUVGB; + static List deferredFileSrc; + static List deferredFileDest; + static List deferredCommandDesc; + + public const string ftraceExe6 = "ftraceRTX.exe"; + public const string ftraceExe1 = "ftrace.exe"; + static string ftraceExe = ftraceExe1; + static bool rtxMode = false; + + public static BakerySector curSector; + static string curSectorName; + + public static int passedFilterFlag = 0; + + enum AdjustUVMode + { + DontChange, + Adjust, + ForceDisableAdjust + } + + static string[] adjustUVOptions = new string[] {"Don't change", "Adjust UV padding", "Remove UV adjustments"}; + + public static event System.EventHandler OnPreFullRender; + public static event System.EventHandler OnPreRenderProbe; + public static event System.EventHandler OnFinishedProbes; + public static event System.EventHandler OnFinishedFullRender; + public static event System.EventHandler OnFinishedReflectionProbes; + + public class ProbeEventArgs : System.EventArgs + { + public Vector3 pos { get; set; } + } + + public static LayerMask forceProbeVisibility; + + // Every LMID -> every channel -> every mask + static List>> lightmapMasks; + static List>> lightmapMaskLMNames; + static List>> lightmapMaskLights; + static List>> lightmapMaskDenoise; +#if UNITY_2017_3_OR_NEWER +#else + static List maskedLights; + PropertyInfo inspectorModeInfo; +#endif + static List lightmapHasColor; + static List lightmapHasMask; + static List lightmapHasDir; + static List lightmapHasRNM; + Scene sceneSavedTestScene; + bool sceneWasSaved = false; + + public bool fixSeams = true; + public bool denoise = true; + public bool denoise2x = false; + public bool encode = true; + + public int padding = 16; + + //public bool bc6h = false; + int encodeMode = 0; + + public bool selectedOnly = false; + bool probesOnlyL1 = false; + public static bool fullSectorRender = false; + + public static bool verbose = true; + public static bool showProgressBar = true; + + public int lightProbeRenderSize = 128; + public int lightProbeReadSize = 16; + public int lightProbeMaxCoeffs = 9; + + public static ftLightmapsStorage storage; + public static Dictionary storages; + + static bool tryFixingSceneView = true; + + // set via experimental UI now + //static bool legacyDenoiser = false; + //static bool oidnDenoiser = false; + static bool foundCompatibleSetup = false; + + const bool alternativeDenoiseDir = true; + + const uint deviceMask = 0xFFFFFFFF; + + List reflectionProbes; + + public ftLightmapsStorage renderSettingsStorage; + + BakeryLightmapGroup currentGroup; + LightingDataAsset newAssetLData; + + public static bool hasAnyProbes = false; + public static bool hasAnyVolumes = false; + static int maxSamplesPerPointLightBatch = 1024; + + public static bool compressVolumes = false; + + Vector2 scrollPos; + + public static ftGlobalStorage gstorage; + static BakeryProjectSettings pstorage; + + public static string scenePath = ""; + public static string scenePathQuoted = ""; +#if !LAUNCH_VIA_DLL + static string dllPath; +#endif + public static string outputPath = "BakeryLightmaps"; + public static string outputPathFull = ""; + + BakeryLightMesh[] All; + BakeryPointLight[] AllP; + BakerySkyLight[] All2; + BakeryDirectLight[] All3; + + const int PASS_LDR = 1; + const int PASS_FLOAT = 2; + const int PASS_HALF = 4; + const int PASS_MASK = 8; + const int PASS_SECONDARY_HALF = 16; + const int PASS_MASK1 = 32; + const int PASS_DIRECTION = 64; + const int PASS_RNM0 = 128; + const int PASS_RNM1 = 256; + const int PASS_RNM2 = 512; + const int PASS_RNM3 = 1024; + + Dictionary lmnameComposed; + + static GUIStyle foldoutStyle; + + static BakeryVolume[] lastFoundBakeableVolumes = null; + + List groupListPlain; + List groupListGIContributingPlain; + + int[] uvBuffOffsets; + int[] uvBuffLengths; + float[] uvSrcBuff; + float[] uvDestBuff; + int[] lmrIndicesOffsets; + int[] lmrIndicesLengths; + int[] lmrIndicesBuff; + int[] lmGroupLODResFlags; + int[] lmGroupMinLOD; + int[] lmGroupLODMatrix; + + public static Material matCubemapToStrip; + + Dictionary shadowmaskGroupIDToChannel; + + static List overlappingLights; + + static LightingDataAsset emptyLDataAsset; + +#if !LAUNCH_VIA_DLL + public static void PatchPath() + { + string currentPath = System.Environment.GetEnvironmentVariable("PATH", System.EnvironmentVariableTarget.Process); + dllPath = System.Environment.CurrentDirectory + Path.DirectorySeparatorChar + "Assets" + Path.DirectorySeparatorChar + "Editor" + Path.DirectorySeparatorChar + "x64"; + if(!currentPath.Contains(dllPath)) + { + System.Environment.SetEnvironmentVariable("PATH", currentPath + Path.PathSeparator + dllPath, System.EnvironmentVariableTarget.Process); + } + } + + static ftRenderLightmap() + { + PatchPath(); + } +#endif + + void ValidateFileAttribs(string file) + { + var attribs = File.GetAttributes(file); + if ((attribs & FileAttributes.ReadOnly) != 0) + { + File.SetAttributes(file, attribs & ~FileAttributes.ReadOnly); + } + } + + static List loadedScenes; + static List loadedScenesEnabled; + static List loadedScenesActive; + static Scene loadedDummyScene; + static bool scenesUnloaded = false; + static public void UnloadScenes() + { + EditorSceneManager.MarkAllScenesDirty(); + EditorSceneManager.SaveOpenScenes(); + + loadedScenes = new List(); + loadedScenesEnabled = new List(); + loadedScenesActive = new List(); + var sceneCount = EditorSceneManager.sceneCount; + var activeScene = EditorSceneManager.GetActiveScene(); + for(int i=0; i Utilities -> Detect optimal settings.", "OK", "Ignore", "Ignore forever"); + if (choice == 0) + { + return false; + } + else if (choice == 2) + { + gstorage.runsOptix5 = true; + EditorUtility.SetDirty(gstorage); + } + } + else if (denoiserType == ftGlobalStorage.DenoiserType.Optix6 && !gstorage.runsOptix6) + { + int choice = EditorUtility.DisplayDialogComplex("Bakery", "This scene has denoiser set to OptiX 6, but you GPU does not seem to support it. Please change the denoiser or re-run Bakery -> Utilities -> Detect optimal settings.", "OK", "Ignore", "Ignore forever"); + if (choice == 0) + { + return false; + } + else if (choice == 2) + { + gstorage.runsOptix6 = true; + EditorUtility.SetDirty(gstorage); + } + } + else if (denoiserType == ftGlobalStorage.DenoiserType.Optix7 && !gstorage.runsOptix7) + { + int choice = EditorUtility.DisplayDialogComplex("Bakery", "This scene has denoiser set to OptiX 7, but you GPU does not seem to support it. Please change the denoiser or re-run Bakery -> Utilities -> Detect optimal settings.", "OK", "Ignore", "Ignore forever"); + if (choice == 0) + { + return false; + } + else if (choice == 2) + { + gstorage.runsOptix7 = true; + EditorUtility.SetDirty(gstorage); + } + } + else if (denoiserType == ftGlobalStorage.DenoiserType.OpenImageDenoise && !gstorage.runsOIDN) + { + int choice = EditorUtility.DisplayDialogComplex("Bakery", "This scene has denoiser set to OpenImageDenoise, but you CPU does not seem to support it. Please change the denoiser or re-run Bakery -> Utilities -> Detect optimal settings.", "OK", "Ignore", "Ignore forever"); + if (choice == 0) + { + return false; + } + else if (choice == 2) + { + gstorage.runsOIDN = true; + EditorUtility.SetDirty(gstorage); + } + } + } + } + } + + return true; + } + + void ValidateOutputPath() + { + // Remove slashes from the end of the path + while (outputPath.Length > 0 && (outputPath[outputPath.Length-1] == '/' || outputPath[outputPath.Length-1] == '\\')) + { + outputPath = outputPath.Substring(0, outputPath.Length-1); + } + var outDir = Application.dataPath + "/" + outputPath; + if (!Directory.Exists(outDir)) Directory.CreateDirectory(outDir); + outputPathFull = outputPath; + + if (curSector != null) + { + curSectorName = curSector.name; + outputPathFull += "/" + curSectorName; + outDir += "/" + curSectorName; + if (!Directory.Exists(outDir)) Directory.CreateDirectory(outDir); + } + else + { + curSectorName = ""; + } + } + + public static double GetTime() + { + return (System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond) / 1000.0; + } + + public static double GetTimeMs() + { + return System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; + } + + static public void LoadScenes() + { + var sceneCount = loadedScenes.Count; + for(int i=0; i(); + if (storage == null) return false; + var list = storage.serverGetFileList; + if (list == null) return false; + + lightmapHasColor = storage.lightmapHasColor; + lightmapHasMask = storage.lightmapHasMask; + lightmapHasDir = storage.lightmapHasDir; + lightmapHasRNM = storage.lightmapHasRNM; + + ftClient.ServerGetData(list); + return true; + } + +#if LAUNCH_VIA_DLL + public static int lastReturnValue = 0; + public static IEnumerator ProcessCoroutine(string app, string args, bool setWorkDir = true) + { + var exeProcess = RunLocalProcess(app+" "+args, setWorkDir); + if (exeProcess == (System.IntPtr)null) + { + DebugLogError(app + " launch failed (see console for details)"); + userCanceled = false; + ProgressBarEnd(); + yield break; + } + while(!IsProcessFinished(exeProcess)) + { + yield return null; + userCanceled = simpleProgressBarCancelled(); + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + } + lastReturnValue = GetProcessReturnValueAndClose(exeProcess); + } +#endif + + int GenerateVBTraceTexLOD(int id) + { + // Write vbTraceTex for LMGroup + var vbtraceTexPosNormalArray = ftBuildGraphics.vbtraceTexPosNormalArray; + var vbtraceTexUVArray = ftBuildGraphics.vbtraceTexUVArray; + var vbtraceTexUVArrayLOD = ftBuildGraphics.vbtraceTexUVArrayLOD; + + var flodInfo = new BinaryReader(File.Open(scenePath + "/lods" + id + ".bin", FileMode.Open, FileAccess.Read)); + flodInfo.BaseStream.Seek(0, SeekOrigin.End); + var numLMs = flodInfo.BaseStream.Position; + flodInfo.BaseStream.Seek(0, SeekOrigin.Begin); + if (lmGroupLODResFlags == null || lmGroupLODResFlags.Length != numLMs) + { + lmGroupLODResFlags = new int[numLMs]; + } + var lodLevels = new int[numLMs]; + for(int i=0; i 0 && lodLevels[i] < 30) + { + //int minLOD = lmGroupMinLOD[id]; + int minLOD = lmGroupMinLOD[i]; + if (lodLevels[i] > minLOD) lodLevels[i] = minLOD; + lmGroupLODResFlags[i] |= 1 << (lodLevels[i] - 1); + } + lmGroupLODMatrix[id * numLMs + i] = lodLevels[i]; + //Debug.LogError("GenerateVBTraceTexLOD: " + id+" to "+i+" = "+lodLevels[i]+" ("+lmGroupLODResFlags[i]+", "+numLMs+")"); + } + flodInfo.Close(); + + var fvbtraceTex2 = new BinaryWriter(File.Open(scenePath + "/vbtraceTex" + id + ".bin", FileMode.Create)); + var numTraceVerts = vbtraceTexUVArray.Count/2; + for(int k=0; k 0) return false; // non-LOD sees itself and LOD0 + } + else + { + //if (otherSceneLodLevel >= 0 && otherSceneLodLevel != curSceneLodLevel) return false; // LOD sees itself and non-LOD + // actually LOD sees non-LOD and other affecting objects we previously calculated + if (otherSceneLodLevel >= 0) + { + var visLists = ftBuildGraphics.lodLevelsVisibleInLodLevel; + if (visLists != null) + { + List visList; + if (visLists.TryGetValue(curSceneLodLevel, out visList)) + { + if (visList != null) + { + if (visList.IndexOf(otherSceneLodLevel) < 0) return false; + } + } + } + } + } + return true; + } + + void GenerateGIParameters(int id, string nm, int bounce, int bounces, bool useDir, int sceneLodLevel) + { + var fgi = new BinaryWriter(File.Open(scenePath + (deferredMode ? "/gi_" + nm + bounce + ".bin" : "/gi.bin"), FileMode.Create)); + fgi.Write(SampleCount(giSamples)); + fgi.Write(giBackFaceWeight); + fgi.Write(bounce == bounces-1 ? "" : "uvalbedo_" + nm + (compressedGBuffer ? ".lz4" : ".dds")); + + int count = 0; + foreach(var lmgroup2 in groupListGIContributingPlain) + { + if (lmgroup2.probes) continue; // nothing is ever affected by probes + if (!GroupAffectedByGroup(sceneLodLevel, lmgroup2.sceneLodLevel)) continue; + count++; + } + fgi.Write(count); + + foreach(var lmgroup2 in groupListGIContributingPlain) + { + if (lmgroup2.probes) continue; // nothing is ever affected by probes + if (!GroupAffectedByGroup(sceneLodLevel, lmgroup2.sceneLodLevel)) continue; + fgi.Write(lmgroup2.id); + + if (giLodModeEnabled) + { + var lod = lmGroupLODMatrix[id * groupListPlain.Count + lmgroup2.id]; + if (lod == 0) + { + fgi.Write(lmgroup2.name + "_diffuse_HDR" + (compressedOutput ? ".lz4" : ".dds")); + } + else if (lod > 0 && lod < 127) + { + //Debug.LogError("GenerateGIParameters: " + id+" to "+lmgroup2.id+" = "+lod+" ("+lmGroupLODResFlags[lmgroup2.id]+", "+groupListPlain.Count+")"); + fgi.Write(lmgroup2.name + "_diffuse_HDR_LOD" + lod + (compressedOutput ? ".lz4" : ".dds")); + } + else + { + fgi.Write(""); + } + } + else + { + fgi.Write(lmgroup2.name + "_diffuse_HDR" + (compressedOutput ? ".lz4" : ".dds")); + } + } + if (useDir) fgi.Write(bounce == bounces - 1 ? (nm + "_lights_Dir" + (compressedOutput ? ".lz4" : ".dds")) : ""); + fgi.Close(); + } + + float Pack4BytesToFloat(int r, int g, int b, int a) + { + // 6 bits precision + + // Move to 0-63 range + r /= 4; + g /= 4; + b /= 4; + a /= 4; + + return (r << 18) | (g << 12) | (b << 6) | a; + } + + float Pack3BytesToFloat(int r, int g, int b) + { + // 8 bits precision + var packed = (r << 16) | (g << 8) | b; + return (packed) / (float)(1 << 24); + } + + void WriteString(BinaryWriter flist, string str) + { + flist.Write(str.Length); + for(int i=0; i= radius + for(int p=0; p bradius) return false; + } + return true; + } + + int GenerateVertexBakedMeshes(int LMID, string lmname, bool hasShadowMask, bool hasDir, bool hasSH) + { + int errCode = 0; + int errCode2 = 0; + int errCode3 = 0; + int errCode4 = 0; + int errCode5 = 0; + int errCode6 = 0; + + //var vertexOffsetLengths = new List(); + int totalVertexCount = 0; + for(int i=0; i 0) + { + DebugLogInfo("Editor window: " + titleBuff.ToString()); + } + else + { + DebugLogInfo("Unable to get editor window name"); + } +#endif + + selectedOnly = false; + probesOnlyL1 = false; + fullSectorRender = curSector != null; + hasAnyVolumes = true; // possibly - ftBuildGraphics will figure it out + progressFunc = RenderLightmapFunc(); + EditorApplication.update += RenderLightmapUpdate; + bakeInProgress = true; + showProgressBar = true; + } + + string Float2String(float val) + { + return ("" + val).Replace(",", "."); // newer Unity versions can break float formatting by incorrectly applying regional settings + } + + public static string progressBarText; + public static float progressBarPercent = 0; + float progressBarStep = 0; + public static bool progressBarEnabled = false; + public static bool userCanceled = false; // can be used externally to check if the bake progress was cancelled + int progressSteps, progressStepsDone; + IEnumerator progressFunc; + public static bool bakeInProgress = false; + void ProgressBarInit(string startText) + { + ProgressBarSetStep(0); + progressBarText = startText; + progressBarEnabled = true; + if (showProgressBar) simpleProgressBarShow("Bakery", progressBarText, progressBarPercent, progressBarStep, false); + } + void ProgressBarSetStep(float step) + { + progressBarStep = step; + } + void ProgressBarShow(string text, float percent, bool onTop) + { + progressBarText = text; + progressBarPercent = percent; + if (showProgressBar) simpleProgressBarShow("Bakery", progressBarText, progressBarPercent, progressBarStep, onTop); + userCanceled = simpleProgressBarCancelled(); + } + public static void ProgressBarEnd(bool freeAreas = true) + { + if (freeAreas) ftBuildGraphics.FreeTemporaryAreaLightMeshes(); + if (scenesUnloaded) LoadScenes(); + + if (revertReflProbesValue) + { + QualitySettings.realtimeReflectionProbes = reflProbesValue; + revertReflProbesValue = false; + } + + if (userCanceled && restoreFromGlobalSector && ftRenderLightmap.instance != null) + { + if (ftRenderLightmap.instance.unloadScenesInDeferredMode) + { + MergeSectorsDeferred(); + } + else + { + MergeSectors(); + } + } + + progressBarEnabled = false; + if (showProgressBar) simpleProgressBarEnd(); + } + void OnInspectorUpdate() + { + Repaint(); + } + string twoChars(int i) + { + if (i < 10) return "0" + i; + return "" + i; + } + void OnGUI() + { + if (progressBarEnabled) + { + return; + } + + if (tryFixingSceneView) + { + FindGlobalStorage(); + if (gstorage != null) + { + // Fix checker preview being incorrectly set for scene view + if (gstorage.checkerPreviewOn && !showChecker) + { + var sceneView = SceneView.lastActiveSceneView; + if (sceneView != null) + { + sceneView.SetSceneViewShaderReplace(null, null); + gstorage.checkerPreviewOn = false; + EditorUtility.SetDirty(gstorage); + } + } + + if (gstorage.rtSceneViewPreviewOn) + { + var sceneView = SceneView.lastActiveSceneView; + if (sceneView != null) + { + sceneView.SetSceneViewShaderReplace(null, null); + gstorage.rtSceneViewPreviewOn = false; + EditorUtility.SetDirty(gstorage); + } + } + } + tryFixingSceneView = false; + } + + int y = 0; + + var headerStyle = EditorStyles.label; + var numberBoxStyle = EditorStyles.numberField; + var textBoxStyle = EditorStyles.textField; + +#if UNITY_2019_3_OR_NEWER + if (EditorGUIUtility.isProSkin) + { + headerStyle = new GUIStyle(EditorStyles.whiteLabel); + } + else + { + headerStyle = new GUIStyle(EditorStyles.label); + } + headerStyle.alignment = TextAnchor.UpperLeft; + headerStyle.padding = new RectOffset(0,0,5,0); + + numberBoxStyle = new GUIStyle(numberBoxStyle); + numberBoxStyle.alignment = TextAnchor.MiddleLeft; + numberBoxStyle.contentOffset = new Vector2(0, -1); + + textBoxStyle = new GUIStyle(textBoxStyle); + textBoxStyle.alignment = TextAnchor.MiddleLeft; + textBoxStyle.contentOffset = new Vector2(0, -1); +#endif + + if (foldoutStyle == null) + { + foldoutStyle = new GUIStyle(EditorStyles.foldout); + //foldoutStyle.fontStyle = FontStyle.Bold; + } + + if (PlayerSettings.colorSpace != ColorSpace.Linear) + { + y += 15; + GUI.BeginGroup(new Rect(10, y, 300, 120), "[Gamma mode detected]", headerStyle); y += 30; +#if UNITY_2019_3_OR_NEWER + int by = 20; +#else + int by = 15; +#endif + if (GUI.Button(new Rect(15, by, 200, 20), "Switch project to linear lighting")) + { + if (EditorUtility.DisplayDialog("Bakery", "Linear lighting mode is essential for getting realistic results. Switching the project may force Unity to reimport assets. It can take some time, depending on project size. Continue?", "OK", "Cancel")) + { + PlayerSettings.colorSpace = ColorSpace.Linear; + } + } + GUI.EndGroup(); + y += 10; + } + + var aboutRect = new Rect(10, y+5, 250, 20); + var linkStyle = new GUIStyle(); + linkStyle.richText = true; + var clr = GUI.contentColor; + GUI.contentColor = Color.blue; + GUI.Label(aboutRect, new GUIContent("Bakery - GPU Lightmapper by Mr F", "Version 1.9. Go to Bakery Wiki"), linkStyle); + GUI.Label(aboutRect, new GUIContent("____________________________", "Go to Bakery Wiki"), linkStyle); + if (Event.current.type == EventType.MouseUp && aboutRect.Contains(Event.current.mousePosition)) + { + Application.OpenURL("https://geom.io/bakery/wiki/"); + } + GUI.contentColor = clr; + y += 15; + + bool simpleWindowIsTooSmall = position.height < 300; + + float scrollHeight = 0; + if (settingsMode >= SettingsMode.Advanced || simpleWindowIsTooSmall) + { + scrollHeight = 620+y+(showAOSettings ? 65 : 15)+(showPaths ? 70 : 0) + (userRenderMode==RenderMode.Shadowmask ? 20 : 0) + 40; + if (showPerf) scrollHeight += 160; +#if UNITY_2019_3_OR_NEWER + scrollHeight += 30; +#endif + scrollHeight += 40;// + (showCompression ? 25*3 : 0); + scrollHeight += 60; + scrollHeight += showTasks2 ? 55+30 : 5; + scrollHeight += showTasks ? (settingsMode == SettingsMode.Experimental ? 140 : 100) : 0; + scrollHeight += 20; + scrollHeight += ftBuildGraphics.texelsPerUnitPerMap ? 120 : 0; + scrollHeight += showCheckerSettings ? 30+20 : 30; + scrollHeight += (showCheckerSettings && showChecker) ? 20 : 0; + scrollHeight += (renderDirMode == RenderDirMode.RNM || renderDirMode == RenderDirMode.SH) ? (showDirWarning ? 60 : 10) : 0; + if (ftBuildGraphics.unwrapUVs) scrollHeight += 20; + if (settingsMode == SettingsMode.Advanced) scrollHeight += 100; + if (settingsMode == SettingsMode.Simple) scrollHeight = this.minSize.y - 30; + if (settingsMode == SettingsMode.Experimental) + { + scrollHeight += 240; + if (ftBuildGraphics.atlasPacker == ftGlobalStorage.AtlasPacker.xatlas) scrollHeight += 60; + if (ftBuildGraphics.unwrapUVs) scrollHeight += 30; + if (denoise) scrollHeight += 20; + if (showNet) scrollHeight += clientMode ? 120 : 30; + } + scrollPos = GUI.BeginScrollView(new Rect(0, 10+y, 270, position.height-20), scrollPos, new Rect(0,10+y,200,scrollHeight)); + } + + if (settingsMode >= SettingsMode.Advanced) + { + this.minSize = new Vector2(position.height >= scrollHeight ? 250 : 270, 700); + } + this.maxSize = new Vector2(this.minSize.x, settingsMode >= SettingsMode.Advanced ? 820 : this.minSize.y + 1); + + GUI.contentColor = new Color(clr.r, clr.g, clr.b, 0.5f); + int hours = lastBakeTime / (60*60); + int minutes = (lastBakeTime / 60) % 60; + int seconds = lastBakeTime % 60; + GUI.Label(new Rect(105, y+10, 130, 20), "Last bake: "+twoChars(hours)+"h "+twoChars(minutes)+"m "+twoChars(seconds)+"s", EditorStyles.miniLabel); + GUI.contentColor = clr; + + GUI.BeginGroup(new Rect(10, 10+y, 300, 340), "Settings", headerStyle); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + var opts = new GUILayoutOption[1]; + opts[0] = GUILayout.Width(225); + settingsMode = (SettingsMode)EditorGUILayout.EnumPopup(settingsMode, opts); + y += 40; + //EditorGUILayout.Space(); + //EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + userRenderMode = (RenderMode)EditorGUILayout.EnumPopup(userRenderMode, opts); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + renderDirMode = (RenderDirMode)EditorGUILayout.EnumPopup(renderDirMode, opts); + + if (settingsMode >= SettingsMode.Advanced) + { + EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + //EditorGUILayout.Space(); + lightProbeMode = (LightProbeMode)EditorGUILayout.EnumPopup(lightProbeMode, opts); + } + + if (settingsMode == SettingsMode.Experimental) + { + EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + ftBuildGraphics.atlasPacker = (ftGlobalStorage.AtlasPacker)EditorGUILayout.EnumPopup(ftBuildGraphics.atlasPacker, opts); + } + + if (settingsMode >= SettingsMode.Advanced) + { + EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + var uvMode = AdjustUVMode.DontChange; + if (ftBuildGraphics.unwrapUVs) + { + uvMode = AdjustUVMode.Adjust; + } + else if (ftBuildGraphics.forceDisableUnwrapUVs) + { + uvMode = AdjustUVMode.ForceDisableAdjust; + } + + uvMode = (AdjustUVMode)EditorGUILayout.Popup((int)uvMode, adjustUVOptions, opts); + + if (uvMode == AdjustUVMode.DontChange) + { + ftBuildGraphics.unwrapUVs = false; + ftBuildGraphics.forceDisableUnwrapUVs = false; + } + else if (uvMode == AdjustUVMode.Adjust) + { + ftBuildGraphics.unwrapUVs = true; + ftBuildGraphics.forceDisableUnwrapUVs = false; + } + else + { + ftBuildGraphics.unwrapUVs = false; + ftBuildGraphics.forceDisableUnwrapUVs = true; + } + } + + if (settingsMode == SettingsMode.Experimental && ftBuildGraphics.unwrapUVs) + { + EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + //EditorGUILayout.Space(); + var prev = unwrapper; + unwrapper = (ftGlobalStorage.Unwrapper)EditorGUILayout.EnumPopup(unwrapper, opts); + if (unwrapper != prev) + { + if (unwrapper == ftGlobalStorage.Unwrapper.xatlas) + { + FindGlobalStorage(); + if (gstorage != null && !gstorage.xatlasWarningShown) + { + gstorage.xatlasWarningShown = true; + EditorUtility.SetDirty(gstorage); + if (!EditorUtility.DisplayDialog("Bakery", "xatlas may provide better UV unwrapping for models with 'Generate lightmap UVs' if 'Adjust UV padding' is enabled in Bakery.\nBut there are several limitations:\n\nTo share a baked scene unwrapped with xatlas, Editor/x64/Bakery/scripts/xatlas folder must be included.\n\nxatlas is native library, so currently any PC opening a baked scene in Unity editor must be on x64 Windows.\n", "Use xatlas", "Cancel")) + { + unwrapper = ftGlobalStorage.Unwrapper.Default; + } + } + } + } + } + + if (settingsMode >= SettingsMode.Advanced) + { + EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + denoiserType = (ftGlobalStorage.DenoiserType)EditorGUILayout.EnumPopup(denoiserType, opts); + } + + GUI.EndGroup(); + + GUI.BeginGroup(new Rect(10, 10+y, 300, 120), "Render mode", headerStyle); + y += 40; + + //bool prevVal = bakeWithNormalMaps; + //bakeWithNormalMaps = GUI.Toggle(new Rect(2, 40, 200, 20), bakeWithNormalMaps, new GUIContent("Bake with normal maps", "Bake normal map effect into lightmaps")); + //y += 20; + + GUI.EndGroup(); + + GUI.BeginGroup(new Rect(10, 10+y, 300, 120), "Directional mode", headerStyle); + y += 40; + + GUI.EndGroup(); + + if (settingsMode >= SettingsMode.Advanced) + { +#if UNITY_2019_3_OR_NEWER +#else + y -= 4; +#endif + GUI.BeginGroup(new Rect(10, 10+y, 300, 120), "Light probe mode", headerStyle); + y += 40; + GUI.EndGroup(); + } + + if (settingsMode == SettingsMode.Experimental) + { +#if UNITY_2019_3_OR_NEWER +#else + y -= 3; +#endif + GUI.BeginGroup(new Rect(10, 10+y, 300, 120), "Atlas packer", headerStyle); + y += 40; + GUI.EndGroup(); + } + + if (settingsMode >= SettingsMode.Advanced) + { +#if UNITY_2019_3_OR_NEWER +#else + if (settingsMode == SettingsMode.Advanced) y -= 3; +#endif + GUI.BeginGroup(new Rect(10, 10+y, 300, 120), "Asset UV processing", headerStyle); + y += 40; + GUI.EndGroup(); + } + + if (settingsMode == SettingsMode.Experimental && ftBuildGraphics.unwrapUVs) + { +#if UNITY_2019_3_OR_NEWER +#else + y -= 3; +#endif + GUI.BeginGroup(new Rect(10, 10+y, 300, 120), "Unwrapper", headerStyle); + y += 40; + GUI.EndGroup(); + } + + if (settingsMode >= SettingsMode.Advanced) + { +#if UNITY_2019_3_OR_NEWER +#else + y -= 3; +#endif + GUI.BeginGroup(new Rect(10, 10+y, 300, 120), "Denoiser", headerStyle); + y += 40; +#if UNITY_2019_3_OR_NEWER + y += 3; +#endif + GUI.EndGroup(); + } + +#if UNITY_2017_1_OR_NEWER + if (userRenderMode == RenderMode.Shadowmask) + { + GUI.BeginGroup(new Rect(10, 10+y, 300, 120), "", headerStyle); + var prevVal = isDistanceShadowmask; + isDistanceShadowmask = GUI.Toggle(new Rect(2, 0, 200, 20), isDistanceShadowmask, new GUIContent("Distance shadowmask", "Use complete real-time shadows close to camera.")); + if (isDistanceShadowmask != prevVal) + { + QualitySettings.shadowmaskMode = isDistanceShadowmask ? ShadowmaskMode.DistanceShadowmask : ShadowmaskMode.Shadowmask; + } + y += 25; + GUI.EndGroup(); + } +#endif + + if (renderDirMode == RenderDirMode.RNM || renderDirMode == RenderDirMode.SH) + { + showDirWarning = EditorGUI.Foldout(new Rect(10,y+10,220,20), showDirWarning, "Directional mode info", foldoutStyle); + if (showDirWarning) + { + var str = renderDirMode + " maps require special shader"; + EditorGUI.HelpBox(new Rect(15,y+30,220,40), str, MessageType.Info); + y += 45; + } + y += 20; + } + + if (settingsMode < SettingsMode.Advanced) + { + this.minSize = new Vector2(250, 310+20-40 + y + 45 + 40 + 20 + (showTasks2 ? 40+30 : 0) + + (userRenderMode == RenderMode.AmbientOcclusionOnly ? (showAOSettings ? 20 : -40) : 0)); + } + + y += 10; + if (settingsMode >= SettingsMode.Advanced) + { + showTasks = EditorGUI.Foldout(new Rect(10, y, 300, 20), showTasks, "Lightmapping tasks", foldoutStyle); + y += 20; + + if (showTasks) + { + int xx = 20; + int yy = y;// - 20; + //GUI.BeginGroup(new Rect(10, y, 300, 160+20), "Lightmapping tasks", headerStyle); + if (settingsMode == SettingsMode.Experimental) + { + forceRebuildGeometry = GUI.Toggle(new Rect(xx, yy, 200, 20), forceRebuildGeometry, new GUIContent("Export geometry and maps", "Exports geometry, textures and lightmap properties to Bakery format. This is required, but if you already rendered the scene, and if no changes to meshes/maps/lightmap resolution took place, you may disable this checkbox to skip this step.")); + yy += 20; + } + //ftBuildGraphics.unwrapUVs = GUI.Toggle(new Rect(xx, yy, 200, 20), ftBuildGraphics.unwrapUVs, new GUIContent("Adjust UV padding", "For meshes with 'Generate lightmap UVs' checkbox enabled, adjusts UVs further to have proper padding between UV islands for each mesh. Model-wide Pack Margin in importer settings is ignored.")); + //yy += 20; + y -= 20; + + adjustSamples = GUI.Toggle(new Rect(xx, yy, 200, 20), adjustSamples, new GUIContent("Adjust sample positions", "Find best sample positions to prevent lighting leaks.")); + yy += 20; + unloadScenesInDeferredMode = GUI.Toggle(new Rect(xx, yy, 200, 20), unloadScenesInDeferredMode, new GUIContent("Unload scenes before render", "Unloads Unity scenes before baking to free up video memory.")); + yy += 20; + if (settingsMode == SettingsMode.Experimental) + { + forceRefresh = GUI.Toggle(new Rect(xx, yy, 200, 20), forceRefresh, new GUIContent("Update unmodified lights", "Update lights that didn't change since last rendering. You can disable this checkbox to skip these lights. Note that it only tracks changes to light objects. If scene geometry changed, then you still need to update all lights.")); + yy += 20; + performRendering = GUI.Toggle(new Rect(xx, yy, 200, 20), performRendering, new GUIContent("Update modified lights and GI", "Update lights that did change since last rendering, plus GI.")); + yy += 20; + } + denoise = GUI.Toggle(new Rect(xx, yy, 200, 20), denoise, new GUIContent("Denoise", "Apply denoising algorithm to lightmaps")); + yy += 20; + if (settingsMode == SettingsMode.Experimental && denoise) + { + denoise2x = GUI.Toggle(new Rect(xx, yy, 200, 20), denoise2x, new GUIContent("Denoise: fix bright edges", "Sometimes the neural net used for denoising may produce bright edges around shadows, like if a sharpening effect was applied. If this option is enabled, Bakery will try to filter them away.")); + yy += 20; + y += 20; + } + fixSeams = GUI.Toggle(new Rect(xx, yy, 200, 20), fixSeams, new GUIContent("Fix Seams", "Fix UV seams on lightmaps")); + //GUI.EndGroup(); + y += (settingsMode == SettingsMode.Experimental ? (135 + 5) : (135 + 30) - 80); + y += 20; + } + } + + GUI.BeginGroup(new Rect(10, y, 300, 340), "Auto-atlasing", headerStyle); + + int ay = 20; + + if (settingsMode >= SettingsMode.Advanced) + { + ftBuildGraphics.splitByScene = GUI.Toggle(new Rect(10, ay, 200, 20), ftBuildGraphics.splitByScene, new GUIContent("Split by scene", "Bake separate lightmap atlases for every scene. Useful to limit the amount of textures loaded when scenes are streamed.")); + ay += 20; + y += 20; + if (settingsMode >= SettingsMode.Experimental) + { + if (ftBuildGraphics.atlasPacker == ftGlobalStorage.AtlasPacker.xatlas) + { + ftBuildGraphics.postPacking = GUI.Toggle(new Rect(10, ay, 200, 20), ftBuildGraphics.postPacking, new GUIContent("Post-packing", "Try to minimize final atlas count by combining different LODs, terrains and regular meshes in one texture.")); + ay += 20; + y += 20; + } + } + + if (settingsMode >= SettingsMode.Advanced) + { + if (ftBuildGraphics.atlasPacker == ftGlobalStorage.AtlasPacker.xatlas) + { + ftBuildGraphics.holeFilling = GUI.Toggle(new Rect(10, ay, 200, 20), ftBuildGraphics.holeFilling, new GUIContent("Hole filling", "Fill holes while packing UV layouts to optimize atlas usage. If disabled, layouts are packed as bounding rectangles.")); + ay += 20; + y += 20; + } + } + + if (settingsMode >= SettingsMode.Experimental) + { + if (ftBuildGraphics.unwrapUVs) + { + ftBuildGraphics.uvPaddingMax = GUI.Toggle(new Rect(10, ay, 200, 20), ftBuildGraphics.uvPaddingMax, new GUIContent("UV padding: increase only", "When finding optimal UV padding for given resolution, the value will never get smaller comparing to previously baked scenes. This is useful when the same model is used across multiple scenes with different lightmap resolution.")); + ay += 20; + y += 20; + } + } + } + + GUI.Label(new Rect(10, ay, 100, 15), new GUIContent("Texels per unit:", "Approximate amount of lightmap pixels per unit allocated for lightmapped objects without Bakery LMGroup component.")); + texelsPerUnit = EditorGUI.FloatField(new Rect(110, ay, 110, 15), texelsPerUnit, numberBoxStyle); + ftBuildGraphics.texelsPerUnit = texelsPerUnit; + ay += 20; + + GUI.Label(new Rect(10, ay, 100, 15), new GUIContent("Max resolution:")); + ay += 20; + GUI.Label(new Rect(10, ay, 100, 15), ""+ftBuildGraphics.maxAutoResolution); + ftBuildGraphics.maxAutoResolution = 1 << (int)GUI.HorizontalSlider(new Rect(50, ay, 170, 15), Mathf.Ceil(Mathf.Log(ftBuildGraphics.maxAutoResolution)/Mathf.Log(2)), 8, 12); + ay += 20; + + if (settingsMode >= SettingsMode.Advanced) + { + GUI.Label(new Rect(10, ay, 100, 15), new GUIContent("Min resolution:")); + ay += 20; + GUI.Label(new Rect(10, ay, 100, 15), ""+ftBuildGraphics.minAutoResolution); + ftBuildGraphics.minAutoResolution = 1 << (int)GUI.HorizontalSlider(new Rect(50, ay, 170, 15), Mathf.Log(ftBuildGraphics.minAutoResolution)/Mathf.Log(2), 4, 12); + y += 40; + ay += 20; + } + + if (settingsMode >= SettingsMode.Advanced) + { + ftBuildGraphics.texelsPerUnitPerMap = EditorGUI.Foldout(new Rect(0, ay, 230, 20), ftBuildGraphics.texelsPerUnitPerMap, "Scale per map type", foldoutStyle); + ay += 20; + if (ftBuildGraphics.texelsPerUnitPerMap) + { + GUI.Label(new Rect(10, ay, 150, 20), new GUIContent("Main lightmap scale:")); + ay += 20; + float actualDiv = 1 << (int)((1.0f - ftBuildGraphics.mainLightmapScale) * 6); + GUI.Label(new Rect(10, ay, 85, 15), "1/"+ actualDiv); + ftBuildGraphics.mainLightmapScale = GUI.HorizontalSlider(new Rect(50, ay, 170, 15), ftBuildGraphics.mainLightmapScale, 0, 1); + ay += 20; + + GUI.Label(new Rect(10, ay, 150, 20), new GUIContent("Shadowmask scale:")); + ay += 20; + actualDiv = 1 << (int)((1.0f - ftBuildGraphics.maskLightmapScale) * 6); + GUI.Label(new Rect(10, ay, 85, 15), "1/"+ actualDiv); + ftBuildGraphics.maskLightmapScale = GUI.HorizontalSlider(new Rect(50, ay, 170, 15), ftBuildGraphics.maskLightmapScale, 0, 1); + ay += 20; + + GUI.Label(new Rect(10, ay, 150, 20), new GUIContent("Direction scale:")); + ay += 20; + actualDiv = 1 << (int)((1.0f - ftBuildGraphics.dirLightmapScale) * 6); + GUI.Label(new Rect(10, ay, 85, 15), "1/"+ actualDiv); + ftBuildGraphics.dirLightmapScale = GUI.HorizontalSlider(new Rect(50, ay, 170, 15), ftBuildGraphics.dirLightmapScale, 0, 1); + ay += 20; + + y += 120; + } + y += 20; + + showCheckerSettings = EditorGUI.Foldout(new Rect(0, ay, 230, 20), showCheckerSettings, "Checker preview", foldoutStyle); + ay += 20; + if (showCheckerSettings) + { + var prevValue = ftSceneView.enabled; + showChecker = GUI.Toggle(new Rect(10, ay, 230, 20), ftSceneView.enabled, new GUIContent("Show checker", "Renders checker in scene view.")); + if (showChecker != prevValue) + { + ftSceneView.ToggleChecker(); + } + ay += 20; + y += 20; + if (showChecker) + { + if (GUI.Button(new Rect(10, ay, 220, 20), "Refresh checker")) + { + ftSceneView.RefreshChecker(); + } + ay += 20; + y += 20; + } + } + y += 20; + } + + GUI.EndGroup(); + y += 45 + 40; + + if (userRenderMode != RenderMode.AmbientOcclusionOnly) + { + y += 5; + GUI.BeginGroup(new Rect(10, y, 300, 300), "Global Illumination", headerStyle); + + GUI.Label(new Rect(10, 20, 70, 15), new GUIContent("Bounces:", "How many times light ray bounces off surfaces. Lower values are faster to render, while higher values ensure light reaches highly occluded places like interiors, caves, etc.")); + var textBounces = GUI.TextField(new Rect(70, 20, 25, 15), "" + bounces, textBoxStyle); + textBounces = Regex.Replace(textBounces, "[^0-9]", ""); + System.Int32.TryParse(textBounces, out bounces); + bounces = (int)GUI.HorizontalSlider(new Rect(100, 20, 120, 15), bounces, 0, 5); + + GUI.Label(new Rect(10, 20+20, 70, 15), new GUIContent("Samples:", "Quality of GI. More samples produce cleaner lighting with less noise.")); + var textGISamples = GUI.TextField(new Rect(70, 20+20, 25, 15), "" + giSamples, textBoxStyle); + textGISamples = Regex.Replace(textGISamples, "[^0-9]", ""); + System.Int32.TryParse(textGISamples, out giSamples); + giSamples = (int)GUI.HorizontalSlider(new Rect(100, 20+20, 120, 15), giSamples, 1, 64); + } + else + { + GUI.BeginGroup(new Rect(10, y-60, 300, 300), "", headerStyle); + } + + GUI.EndGroup(); + if (userRenderMode != RenderMode.AmbientOcclusionOnly) y += 60; + + if (settingsMode == SettingsMode.Simple && userRenderMode == RenderMode.AmbientOcclusionOnly) + { + showAOSettings = true; + showAOSettings = EditorGUI.Foldout(new Rect(10, y, 300, 20), showAOSettings, "Ambient occlusion"); + if (showAOSettings) + { + int xx = 15; + int yy = y + 10; + int ww = 110; + + GUI.Label(new Rect(10+xx, 15+yy, 100, 15), new GUIContent("Intensity:", "AO visibility. Disabled if set to 0.")); + hackAOIntensity = EditorGUI.FloatField(new Rect(95+xx, 15+yy, ww, 15), hackAOIntensity, numberBoxStyle); + + GUI.Label(new Rect(10+xx, 30+yy, 100, 15), new GUIContent("Radius:", "AO radius")); + hackAORadius = EditorGUI.FloatField(new Rect(95+xx, 30+yy, ww, 15), hackAORadius, numberBoxStyle); + + GUI.Label(new Rect(10+xx, 45+yy, 100, 15), new GUIContent("Samples:", "Affects the quality of AO")); + hackAOSamples = EditorGUI.IntField(new Rect(95+xx, 45+yy, ww, 15), hackAOSamples, numberBoxStyle); + + y += 60; + } + y += 20; + } + else if (settingsMode >= SettingsMode.Advanced) + { + //showHacks = EditorGUI.Foldout(new Rect(10, y, 300, 300), showHacks, "Hacks"); + //y += 20; + //if (showHacks) + { + GUI.BeginGroup(new Rect(10, y, 300, 300), "Hacks", headerStyle); + + int yy = 20; + GUI.Label(new Rect(10, yy, 100, 15), new GUIContent("Emissive boost:", "Multiplies light from emissive surfaces.")); + hackEmissiveBoost = EditorGUI.FloatField(new Rect(110, yy, 110, 15), hackEmissiveBoost, numberBoxStyle); + yy += 20; + + GUI.Label(new Rect(10, yy, 100, 15), new GUIContent("Indirect boost:", "Multiplies indirect intensity for all lights.")); + hackIndirectBoost = EditorGUI.FloatField(new Rect(110, yy, 110, 15), hackIndirectBoost, numberBoxStyle); + yy += 20; + + GUI.Label(new Rect(10, yy, 120, 20), new GUIContent("Backface GI:", "How much light is emitted via back faces from 0 (black) to 1 (equals to front face).")); + giBackFaceWeight = EditorGUI.FloatField(new Rect(110, yy, 110, 15), giBackFaceWeight, numberBoxStyle); + yy += 20; + + showAOSettings = EditorGUI.Foldout(new Rect(10, yy, 300, 20), showAOSettings, "Ambient occlusion"); + yy += 20; + y += 15+40; + if (showAOSettings) + { + int xx = 15; + yy = 45+40; + int ww = 110; + + GUI.Label(new Rect(10+xx, 15+yy, 100, 15), new GUIContent("Intensity:", "AO visibility. Disabled if set to 0.")); + hackAOIntensity = EditorGUI.FloatField(new Rect(95+xx, 15+yy, ww, 15), hackAOIntensity, numberBoxStyle); + + GUI.Label(new Rect(10+xx, 30+yy, 100, 15), new GUIContent("Radius:", "AO radius")); + hackAORadius = EditorGUI.FloatField(new Rect(95+xx, 30+yy, ww, 15), hackAORadius, numberBoxStyle); + + GUI.Label(new Rect(10+xx, 45+yy, 100, 15), new GUIContent("Samples:", "Affects the quality of AO")); + hackAOSamples = EditorGUI.IntField(new Rect(95+xx, 45+yy, ww, 15), hackAOSamples, numberBoxStyle); + + y += 50; + } + + GUI.EndGroup(); + y += 50; + } + + showPerf = EditorGUI.Foldout(new Rect(10, y, 300, 20), showPerf, "Performance", foldoutStyle); + y += 20; + if (showPerf) + { + int xx = 10; + + var prev = rtxMode; + rtxMode = + GUI.Toggle(new Rect(xx, y, 200, 20), rtxMode, + new GUIContent(" RTX mode", "Enables RTX hardware acceleration (requires supported hardware).")); + if (prev != rtxMode) + { + ftraceExe = rtxMode ? ftraceExe6 : ftraceExe1; + if (rtxMode) ftBuildGraphics.exportTerrainAsHeightmap = false; + } + y += 20; + + ftBuildGraphics.exportTerrainTrees = + GUI.Toggle(new Rect(xx, y, 200, 20), ftBuildGraphics.exportTerrainTrees, + new GUIContent(" Export terrain trees", "If enabled, painted terrain trees will affect lighting. Trees themselves will not be baked.")); + y += 20; + + prev = ftBuildGraphics.exportTerrainAsHeightmap; + //if (settingsMode >= SettingsMode.Experimental) + //{ + ftBuildGraphics.exportTerrainAsHeightmap = + GUI.Toggle(new Rect(xx, y, 200, 20), ftBuildGraphics.exportTerrainAsHeightmap, + new GUIContent(" Terrain optimization", "If enabled, terrains use separate ray tracing technique to take advantage of their heightfield geometry. Otherwise they are treated like any other mesh.")); + if (prev != ftBuildGraphics.exportTerrainAsHeightmap) + { + if (ftBuildGraphics.exportTerrainAsHeightmap) + { + rtxMode = false; + ftraceExe = ftraceExe1; + } + } + y += 20; + //} + + if (settingsMode >= SettingsMode.Experimental) + { +#if UNITY_2020_1_OR_NEWER + compressVolumes = + GUI.Toggle(new Rect(xx, y, 200, 20), compressVolumes, + new GUIContent(" Compress volumes", "Apply texture compression to volume 3D textures and switch Bakery shaders to a corresponding sampling mode. Not recommended for very low resolution volumes. Volume size may be increased to be a multiple of 4.")); +#else + GUI.enabled = false; + compressVolumes = + GUI.Toggle(new Rect(xx, y, 200, 20), compressVolumes, + new GUIContent(" Compress volumes", "(Requires Unity 2020.1 or newer) Apply texture compression to volume 3D textures and switch Bakery shaders to a corresponding sampling mode. Not recommended for very low resolution volumes. Volume size may be increased to be a multiple of 4.")); + GUI.enabled = true; +#endif + y += 20; + } + + GUI.Label(new Rect(10, y, 150, 20), new GUIContent("Samples multiplier", "Multiplies all shadow and GI samples by the specified factor. Use this to quickly change between draft and final quality.")); + y += 20; + GUI.Label(new Rect(10, y, 85, 15), "1/"+ sampleDivisor); + const int maxSampleDivisor = 8; + sampleDivisor = (int)GUI.HorizontalSlider(new Rect(50, y, 170, 15), (float)(maxSampleDivisor - (sampleDivisor-1)), 1, maxSampleDivisor); + sampleDivisor = maxSampleDivisor - (sampleDivisor-1); + y += 20; + + GUI.BeginGroup(new Rect(xx, y, 300, 120), "GI VRAM optimization", headerStyle); + y += 20; + GUI.EndGroup(); + giLodMode = (GILODMode)EditorGUI.EnumPopup(new Rect(xx, y, 225, 25), giLodMode); + y += 20; + + GUI.BeginGroup(new Rect(xx, y, 300, 300), "Tile size", headerStyle); + GUI.Label(new Rect(10, 20, 70, 15), new GUIContent("" + tileSize, "Lightmaps are split into smaller tiles and each tile is processed by the GPU without interruputions. Changing tile size therefore balances between system responsiveness and baking speed. Because GPU is shared by all running processes, baking with big tile size can make everything slow, but also gets the job done faster.")); + tileSize = 1 << (int)GUI.HorizontalSlider(new Rect(50, 20, 170, 15), Mathf.Log(tileSize)/Mathf.Log(2), 5, 12); + GUI.EndGroup(); + y += 45; + } + } + + + if (settingsMode >= SettingsMode.Advanced) + { + + } + else + { + GUI.BeginGroup(new Rect(10, y, 300, 300), "GPU priority", headerStyle); + string priorityName = ""; + if (tileSize > 512) + { + if ((int)priority!=3) priority = 3; // >= 1024 very high + priorityName = "Very high"; + } + else if (tileSize > 256) + { + if ((int)priority!=2) priority = 2; // >= 512 high + priorityName = "High"; + } + else if (tileSize > 64) + { + if ((int)priority!=1) priority = 1; // >= 128 low + priorityName = "Low"; + } + else + { + if ((int)priority!=0) priority = 0; // == 32 very low + priorityName = "Very low"; + } + GUI.Label(new Rect(10, 20, 75, 20), new GUIContent("" + priorityName, "Balance between system responsiveness and baking speed. Because GPU is shared by all running processes, baking on high priority can make everything slow, but also gets the job done faster.")); + priority = GUI.HorizontalSlider(new Rect(80, 20, 140, 15), priority, 0, 3); + if ((int)priority == 0) + { + tileSize = 32; + } + else if ((int)priority == 1) + { + tileSize = 128; + } + else if ((int)priority == 2) + { + tileSize = 512; + } + else + { + tileSize = 1024; + } + GUI.EndGroup(); + y += 50; + } + + if (scenePath == "") scenePath = System.Environment.GetEnvironmentVariable("TEMP", System.EnvironmentVariableTarget.Process) + "\\frender"; + if (settingsMode >= SettingsMode.Advanced) + { + showPaths = EditorGUI.Foldout(new Rect(10, y, 230, 20), showPaths, "Output options", foldoutStyle); + y += 20; + + if (showPaths) + { + if (GUI.Button(new Rect(10, y, 230, 40), new GUIContent("Temp path:\n" + scenePath, "Specify folder where temporary data will be stored. Using SSD can speed up rendering a bit comparing to HDD."))) + { + scenePath = EditorUtility.OpenFolderPanel("Select temp folder", scenePath, "frender"); + } + y += 50; + + useScenePath = EditorGUI.ToggleLeft( new Rect( 10, y, 230, 20 ), new GUIContent( "Use scene named output path", "Create the lightmaps in a subfolder named the same as the scene" ), useScenePath ); + y += 25; + if ( !useScenePath ) { + GUI.Label(new Rect(10, y, 100, 16), new GUIContent("Output path:", "Specify folder where lightmaps data will be stored (relative to Assets)")); + outputPath = EditorGUI.TextField(new Rect(85, y, 155, 18), outputPath, textBoxStyle); + y += 25; + } else { + // AMW - don't override the outputPath if we currently have the temp scene open. + // this seemed to happen during lightprobe bakes and the lightprobes would end up in the _tempScene path + string currentScenePath = EditorSceneManager.GetActiveScene().path; + if ( currentScenePath.ToLower().Contains( "_tempscene.unity" ) == false ) { + outputPath = currentScenePath; + if ( string.IsNullOrEmpty( outputPath ) ) { + outputPath = "BakeryLightmaps"; + } else { + // strip "Assets/" and the file extension + if (outputPath.Length > 7 && outputPath.Substring(0,7).ToLower() == "assets/") outputPath = outputPath.Substring(7); + if (outputPath.Length > 6 && outputPath.Substring(outputPath.Length-6).ToLower() == ".unity") + outputPath = outputPath.Substring(0, outputPath.Length-6); + } + } + } + } + } + + if (settingsMode >= SettingsMode.Experimental) + { + showNet = EditorGUI.Foldout(new Rect(10, y, 230, 20), showNet, "Network baking", foldoutStyle); + y += 20; + + if (showNet) + { + clientMode = EditorGUI.ToggleLeft( new Rect( 10, y, 230, 20 ), new GUIContent( "Bake on remote server", "Enable network baking" ), clientMode ); + y += 20; + if (clientMode) + { + GUI.Label(new Rect(10, y, 100, 16), new GUIContent("IP address:", "Server address where ftServer.exe is launched")); + ftClient.serverAddress = EditorGUI.TextField(new Rect(85, y, 155, 18), ftClient.serverAddress, textBoxStyle); + y += 20; + + if (ftClient.lastServerMsgIsError) ftClient.Disconnect(); + + if (!ftClient.connectedToServer) + { + if (GUI.Button(new Rect(10, y, 230, 30), "Connect to server")) + { + ftClient.ConnectToServer(); + } + } + else + { + ftClient.Update(); + if (GUI.Button(new Rect(10, y, ftClient.serverGetDataMode ? 230 : (230/2), 30), "Disconnect")) + { + ftClient.Disconnect(); + ftClient.lastServerMsg = "Server: no data"; + ftClient.lastServerMsgIsError = false; + ftClient.lastServerErrorCode = 0; + } + if (!ftClient.serverGetDataMode) + { + if (ftClient.serverMustRefreshData) + { + CollectStorages(); + var groupList = new List(); + var groupListGIContributing = new List(); + CollectGroups(groupList, groupListGIContributing, false); + ftClient.serverMustRefreshData = false; + var apply = ApplyBakedData(); + while(apply.MoveNext()) {} + } + if (GUI.Button(new Rect(230/2+10, y, 230/2, 30), "Get data")) + { + if (ftClient.lastServerScene.Length == 0) + { + DebugLogError("No baked scene is found on the server."); + } + else if (ftClient.lastServerScene != EditorSceneManager.GetActiveScene().name) + { + DebugLogError("Current active scene doesn't match the one on the server."); + } + else if (ftClient.serverGetDataMode) + { + DebugLogInfo("Data is being downloaded"); + } + else + { + if (!ServerGetData()) + { + DebugLogError("Failed to find the list of files to download."); + } + } + } + } + } + + y += 30; + + var msg = ftClient.lastServerMsg; + if (ftClient.lastServerScene.Length > 0) msg += "\nScene: "+ftClient.lastServerScene; + if (ftClient.serverGetDataMode) msg += "\nDownloading: " + System.Math.Min(ftClient.serverGetFileIterator+1, ftClient.serverGetFileList.Count) + "/" + ftClient.serverGetFileList.Count; + EditorGUI.HelpBox(new Rect(15,y+5,220,40), msg, ftClient.lastServerMsgIsError ? MessageType.Error : MessageType.Info); + y += 40; + } + y += 10; + } + } + + ftBuildGraphics.scenePath = scenePath; + scenePathQuoted = "\"" + scenePath + "\""; + + /*if (settingsMode >= SettingsMode.Advanced) + { + showCompression = EditorGUI.Foldout(new Rect(10, y, 230, 20), showCompression, "Compression", foldoutStyle); + y += 20; + if (showCompression) + { + int xx = 10; + float prevWidth = EditorGUIUtility.labelWidth; + EditorGUIUtility.labelWidth = 45f; + lightmapCompressionColor = (TextureImporterFormat)EditorGUI.EnumPopup( new Rect( xx, y, 240-xx, 20 ), new GUIContent( "Color:", "Set the default compression for the lightmap textures" ), lightmapCompressionColor ); + y += 25; + //EditorGUIUtility.labelWidth = 85f; + lightmapCompressionMask = (TextureImporterFormat)EditorGUI.EnumPopup( new Rect( xx, y, 240-xx, 20 ), new GUIContent( "Mask:", "Set the default compression for the lightmap textures" ), lightmapCompressionMask ); + y += 25; + //EditorGUIUtility.labelWidth = 65f; + lightmapCompressionDir = (TextureImporterFormat)EditorGUI.EnumPopup( new Rect( xx, y, 240-xx, 20 ), new GUIContent( "Dir:", "Set the default compression for the lightmap textures" ), lightmapCompressionDir ); + EditorGUIUtility.labelWidth = prevWidth; + y += 25; + } + }*/ + + /*if (settingsMode == SettingsMode.Experimental) + { + GUI.BeginGroup(new Rect(10, y, 300, 300), "Output texture type", headerStyle); + encodeMode = GUI.SelectionGrid(new Rect(10, 20, 210, 20), encodeMode, selStrings, 2); + GUI.EndGroup(); + y += 50; + }*/ + ftBuildGraphics.overwriteExtensionCheck = ".hdr";//bc6h ? ".asset" : ".hdr"; + + if (settingsMode >= SettingsMode.Advanced) + { + curSector = EditorGUI.ObjectField(new Rect(10, y, 230, 16), curSector, typeof(BakerySector), true) as BakerySector; + y += 25; + } + + + if (GUI.Button(new Rect(10, y, 230, 30), "Render")) + { + ValidateOutputPath(); + RenderButton(); + } + y += 35; + + if (settingsMode >= SettingsMode.Experimental) + { + if (GUI.Button(new Rect(10, y, 230, 30), "Render Selected Groups")) + { + if (!Application.isPlaying) + { + ValidateOutputPath(); + if (!TestSystemSpecs()) return; + selectedOnly = true; + probesOnlyL1 = false; + fullSectorRender = false; + hasAnyVolumes = true; // possibly - ftBuildGraphics will figure it out + progressFunc = RenderLightmapFunc(); + EditorApplication.update += RenderLightmapUpdate; + bakeInProgress = true; + } + } + y += 35; + } + + if (GUI.Button(new Rect(10, y, 230, 30), "Render Light Probes")) + { + ValidateOutputPath(); + RenderLightProbesButton(); + } + y += 35; + + if (GUI.Button(new Rect(10, y, 230, 30), "Render Reflection Probes")) + { + ValidateOutputPath(); + RenderReflectionProbesButton(); + } + y += 35; + + if (GUI.Button(new Rect(10, y, 230, 30), "Update Skybox Probe")) + { + if (!Application.isPlaying) + { + ValidateOutputPath(); + DynamicGI.UpdateEnvironment(); + + var rgo = new GameObject(); + var r = rgo.AddComponent(); + r.resolution = 256; + r.clearFlags = UnityEngine.Rendering.ReflectionProbeClearFlags.Skybox; + r.cullingMask = 0; + r.mode = UnityEngine.Rendering.ReflectionProbeMode.Custom; + + var assetName = GenerateLightingDataAssetName(); + var outName = "Assets/" + outputPath + "/" + assetName + "_sky.exr"; + if (File.Exists(outName)) ValidateFileAttribs(outName); + Lightmapping.BakeReflectionProbe(r, outName); + + AssetDatabase.Refresh(); + RenderSettings.customReflection = AssetDatabase.LoadAssetAtPath(outName, typeof(Cubemap)) as Cubemap; + RenderSettings.defaultReflectionMode = UnityEngine.Rendering.DefaultReflectionMode.Custom; + DestroyImmediate(rgo); + } + } + y += 30; + + if (settingsMode >= SettingsMode.Experimental) + { + //showTasks2 = EditorGUI.Foldout(new Rect(10, y-5, 300, 20), showTasks2, "Light probe tasks", foldoutStyle); + //y += 20 - (showTasks2 ? 10 : 5); + //if (showTasks2) + { + var prevValue = usesRealtimeGI; + usesRealtimeGI = GUI.Toggle(new Rect(10, y+5, 230, 20), usesRealtimeGI, new GUIContent("Combine with Enlighten real-time GI", "When Render button is pressed, first Enlighten real-time GI will be calculated. Then Bakery will bake regular lightmaps. Static and real-time GI will be combined.")); + if (prevValue != usesRealtimeGI) + { + //Lightmapping.realtimeGI = usesRealtimeGI; + } + y += 20; + } + } + + //if (settingsMode >= SettingsMode.Advanced) + { + useUnityForOcclsusionProbes = GUI.Toggle(new Rect(10, y+5, 230, 20), useUnityForOcclsusionProbes, new GUIContent("Occlusion probes", "When Render Light Probes is pressed, lets Unity bake occlusion probes using currently selected built-in lightmapper. Occlusion probes prevent dynamic objects from getting lit in shadowed areas. Currently there is no way to use custom occlusion probes in Unity, and it has to call its own lightmappers to do the job.")); + y += 25; + } + + if (settingsMode >= SettingsMode.Advanced) + { + beepOnFinish = GUI.Toggle(new Rect(10, y, 230, 20), beepOnFinish, new GUIContent("Beep on finish", "Plays a sound when the bake is done.")); + y += 25; + } + + showTasks2 = EditorGUI.Foldout(new Rect(10, y, 300, 20), showTasks2, "Warnings", foldoutStyle); + y += 12+2; + if (showTasks2) + { + checkOverlaps = GUI.Toggle(new Rect(10, y, 200, 20), checkOverlaps, new GUIContent("UV validation", "Checks for any incorrect missing or overlapping UVs")); + y += 15; + ftBuildGraphics.memoryWarning = GUI.Toggle(new Rect(10, y, 200, 20), ftBuildGraphics.memoryWarning, new GUIContent("Memory check", "Calculate approximate amount of required video memory and ask to continue")); + y += 15; + ftBuildGraphics.overwriteWarning = GUI.Toggle(new Rect(10, y, 200, 20), ftBuildGraphics.overwriteWarning, new GUIContent("Overwrite check", "Check and ask if any existing lightmaps are going to be overwritten")); + y += 15; + samplesWarning = GUI.Toggle(new Rect(10, y, 200, 20), samplesWarning, new GUIContent("Sample count check", "Checks if sample values for lights/GI/AO are in reasonable range")); + y += 15; + prefabWarning = GUI.Toggle(new Rect(10, y, 200, 20), prefabWarning, new GUIContent("Lightmapped prefab validation", "Checks if any prefabs are going to be overwritten and if there is anything preventing from baking them")); + } + + if (settingsMode >= SettingsMode.Advanced || simpleWindowIsTooSmall) + { + GUI.EndScrollView(); + } + + if (ftLightmaps.mustReloadRenderSettings) + { + ftLightmaps.mustReloadRenderSettings = false; + OnEnable(); + } + + SaveRenderSettings(); + } + + public void SaveRenderSettings() + { + var scenePathToSave = scenePath; + if (scenePathToSave == System.Environment.GetEnvironmentVariable("TEMP", System.EnvironmentVariableTarget.Process) + "\\frender") + { + scenePathToSave = ""; + } + + if (renderSettingsStorage == null) return; + + FindGlobalStorage(); + if (gstorage != null) + { + if (gstorage.renderSettingsTempPath != scenePathToSave) + { + gstorage.renderSettingsTempPath = scenePathToSave; + EditorUtility.SetDirty(gstorage); + } + } + + if ( + renderSettingsStorage.renderSettingsBounces != bounces || + renderSettingsStorage.renderSettingsGISamples != giSamples || + renderSettingsStorage.renderSettingsGIBackFaceWeight != giBackFaceWeight || + renderSettingsStorage.renderSettingsTileSize != tileSize || + renderSettingsStorage.renderSettingsPriority != priority || + renderSettingsStorage.renderSettingsTexelsPerUnit != texelsPerUnit || + renderSettingsStorage.renderSettingsForceRefresh != forceRefresh || + renderSettingsStorage.renderSettingsForceRebuildGeometry != forceRebuildGeometry || + renderSettingsStorage.renderSettingsPerformRendering != performRendering || + renderSettingsStorage.renderSettingsUserRenderMode != (int)userRenderMode || + renderSettingsStorage.renderSettingsSettingsMode != (int)settingsMode || + renderSettingsStorage.renderSettingsFixSeams != fixSeams || + renderSettingsStorage.renderSettingsDenoise != denoise || + renderSettingsStorage.renderSettingsDenoise2x != denoise2x || + renderSettingsStorage.renderSettingsEncode != encode || + renderSettingsStorage.renderSettingsEncodeMode != encodeMode || + renderSettingsStorage.renderSettingsOverwriteWarning != ftBuildGraphics.overwriteWarning || + renderSettingsStorage.renderSettingsAutoAtlas != ftBuildGraphics.autoAtlas || + renderSettingsStorage.renderSettingsUnwrapUVs != ftBuildGraphics.unwrapUVs || + renderSettingsStorage.renderSettingsForceDisableUnwrapUVs != ftBuildGraphics.forceDisableUnwrapUVs || + renderSettingsStorage.renderSettingsMaxAutoResolution != ftBuildGraphics.maxAutoResolution || + renderSettingsStorage.renderSettingsMinAutoResolution != ftBuildGraphics.minAutoResolution || + renderSettingsStorage.renderSettingsUnloadScenes != unloadScenesInDeferredMode || + renderSettingsStorage.renderSettingsAdjustSamples != adjustSamples || + renderSettingsStorage.renderSettingsGILODMode != (int)giLodMode || + renderSettingsStorage.renderSettingsGILODModeEnabled != giLodModeEnabled || + renderSettingsStorage.renderSettingsCheckOverlaps != checkOverlaps || + renderSettingsStorage.renderSettingsOutPath != outputPath || + renderSettingsStorage.renderSettingsUseScenePath != useScenePath || + //renderSettingsStorage.renderSettingsTempPath != scenePathToSave || + renderSettingsStorage.renderSettingsHackEmissiveBoost != hackEmissiveBoost || + renderSettingsStorage.renderSettingsHackIndirectBoost != hackIndirectBoost || + renderSettingsStorage.renderSettingsHackAOIntensity != hackAOIntensity || + renderSettingsStorage.renderSettingsHackAORadius != hackAORadius || + renderSettingsStorage.renderSettingsHackAOSamples != hackAOSamples || + renderSettingsStorage.renderSettingsShowAOSettings != showAOSettings || + renderSettingsStorage.renderSettingsShowTasks != showTasks || + renderSettingsStorage.renderSettingsShowTasks2 != showTasks2 || + renderSettingsStorage.renderSettingsShowPaths != showPaths || + renderSettingsStorage.renderSettingsShowNet != showNet || + renderSettingsStorage.renderSettingsShowPerf != showPerf || + //renderSettingsStorage.renderSettingsShowCompression != showCompression || + renderSettingsStorage.renderSettingsTexelsPerMap != ftBuildGraphics.texelsPerUnitPerMap || + renderSettingsStorage.renderSettingsTexelsColor != ftBuildGraphics.mainLightmapScale || + renderSettingsStorage.renderSettingsTexelsMask != ftBuildGraphics.maskLightmapScale || + renderSettingsStorage.renderSettingsTexelsDir != ftBuildGraphics.dirLightmapScale || + renderSettingsStorage.renderSettingsOcclusionProbes != useUnityForOcclsusionProbes || + renderSettingsStorage.renderSettingsBeepOnFinish != beepOnFinish || + renderSettingsStorage.renderSettingsDistanceShadowmask != isDistanceShadowmask || + renderSettingsStorage.renderSettingsShowDirWarning != showDirWarning || + renderSettingsStorage.renderSettingsRenderDirMode != (int)renderDirMode || + renderSettingsStorage.renderSettingsShowCheckerSettings != showCheckerSettings || + renderSettingsStorage.usesRealtimeGI != usesRealtimeGI || + renderSettingsStorage.renderSettingsSamplesWarning != samplesWarning || + renderSettingsStorage.renderSettingsPrefabWarning != prefabWarning || + renderSettingsStorage.renderSettingsSplitByScene != ftBuildGraphics.splitByScene || + renderSettingsStorage.renderSettingsExportTerrainAsHeightmap != ftBuildGraphics.exportTerrainAsHeightmap || + renderSettingsStorage.renderSettingsExportTerrainTrees != ftBuildGraphics.exportTerrainTrees || + renderSettingsStorage.renderSettingsRTXMode != rtxMode || + renderSettingsStorage.renderSettingsLightProbeMode != (int)lightProbeMode || + renderSettingsStorage.renderSettingsClientMode != clientMode || + renderSettingsStorage.renderSettingsServerAddress != ftClient.serverAddress || + renderSettingsStorage.renderSettingsUVPaddingMax != ftBuildGraphics.uvPaddingMax || + renderSettingsStorage.renderSettingsPostPacking != ftBuildGraphics.postPacking || + renderSettingsStorage.renderSettingsHoleFilling != ftBuildGraphics.holeFilling || + renderSettingsStorage.renderSettingsSampleDiv != sampleDivisor || + renderSettingsStorage.renderSettingsUnwrapper != (int)unwrapper || + renderSettingsStorage.renderSettingsDenoiserType != (int)denoiserType || + //renderSettingsStorage.renderSettingsLegacyDenoiser != legacyDenoiser || + renderSettingsStorage.renderSettingsAtlasPacker != ftBuildGraphics.atlasPacker || + renderSettingsStorage.renderSettingsCompressVolumes != compressVolumes || + renderSettingsStorage.renderSettingsSector != curSector + ) + { + Undo.RecordObject(renderSettingsStorage, "Change Bakery settings"); + renderSettingsStorage.renderSettingsBounces = bounces; + renderSettingsStorage.renderSettingsGISamples = giSamples; + renderSettingsStorage.renderSettingsGIBackFaceWeight = giBackFaceWeight; + renderSettingsStorage.renderSettingsTileSize = tileSize; + renderSettingsStorage.renderSettingsPriority = priority; + renderSettingsStorage.renderSettingsTexelsPerUnit = texelsPerUnit; + renderSettingsStorage.renderSettingsForceRefresh = forceRefresh; + renderSettingsStorage.renderSettingsForceRebuildGeometry = forceRebuildGeometry; + renderSettingsStorage.renderSettingsPerformRendering = performRendering; + renderSettingsStorage.renderSettingsUserRenderMode = (int)userRenderMode; + renderSettingsStorage.renderSettingsSettingsMode = (int)settingsMode; + renderSettingsStorage.renderSettingsFixSeams = fixSeams; + renderSettingsStorage.renderSettingsDenoise = denoise; + renderSettingsStorage.renderSettingsDenoise2x = denoise2x; + renderSettingsStorage.renderSettingsEncode = encode; + renderSettingsStorage.renderSettingsEncodeMode = encodeMode; + renderSettingsStorage.renderSettingsOverwriteWarning = ftBuildGraphics.overwriteWarning; + renderSettingsStorage.renderSettingsAutoAtlas = ftBuildGraphics.autoAtlas; + renderSettingsStorage.renderSettingsUnwrapUVs = ftBuildGraphics.unwrapUVs; + renderSettingsStorage.renderSettingsForceDisableUnwrapUVs = ftBuildGraphics.forceDisableUnwrapUVs; + renderSettingsStorage.renderSettingsMaxAutoResolution = ftBuildGraphics.maxAutoResolution; + renderSettingsStorage.renderSettingsMinAutoResolution = ftBuildGraphics.minAutoResolution; + renderSettingsStorage.renderSettingsUnloadScenes = unloadScenesInDeferredMode; + renderSettingsStorage.renderSettingsAdjustSamples = adjustSamples; + renderSettingsStorage.renderSettingsGILODMode = (int)giLodMode; + renderSettingsStorage.renderSettingsGILODModeEnabled = giLodModeEnabled; + renderSettingsStorage.renderSettingsCheckOverlaps = checkOverlaps; + renderSettingsStorage.renderSettingsOutPath = outputPath; + renderSettingsStorage.renderSettingsUseScenePath = useScenePath; + //renderSettingsStorage.renderSettingsTempPath = scenePathToSave; + renderSettingsStorage.renderSettingsHackEmissiveBoost = hackEmissiveBoost; + renderSettingsStorage.renderSettingsHackIndirectBoost = hackIndirectBoost; + renderSettingsStorage.renderSettingsHackAOIntensity = hackAOIntensity; + renderSettingsStorage.renderSettingsHackAORadius = hackAORadius; + renderSettingsStorage.renderSettingsHackAOSamples = hackAOSamples; + renderSettingsStorage.renderSettingsShowAOSettings = showAOSettings; + renderSettingsStorage.renderSettingsShowTasks = showTasks; + renderSettingsStorage.renderSettingsShowTasks2 = showTasks2; + renderSettingsStorage.renderSettingsShowPaths = showPaths; + renderSettingsStorage.renderSettingsShowNet = showNet; + renderSettingsStorage.renderSettingsShowPerf = showPerf; + //renderSettingsStorage.renderSettingsShowCompression = showCompression; + renderSettingsStorage.renderSettingsTexelsPerMap = ftBuildGraphics.texelsPerUnitPerMap; + renderSettingsStorage.renderSettingsTexelsColor = ftBuildGraphics.mainLightmapScale; + renderSettingsStorage.renderSettingsTexelsMask = ftBuildGraphics.maskLightmapScale; + renderSettingsStorage.renderSettingsTexelsDir = ftBuildGraphics.dirLightmapScale; + renderSettingsStorage.renderSettingsOcclusionProbes = useUnityForOcclsusionProbes; + renderSettingsStorage.renderSettingsBeepOnFinish = beepOnFinish; + renderSettingsStorage.renderSettingsDistanceShadowmask = isDistanceShadowmask; + renderSettingsStorage.renderSettingsShowDirWarning = showDirWarning; + renderSettingsStorage.renderSettingsRenderDirMode = (int)renderDirMode; + renderSettingsStorage.renderSettingsShowCheckerSettings = showCheckerSettings; + renderSettingsStorage.usesRealtimeGI = usesRealtimeGI; + renderSettingsStorage.renderSettingsSamplesWarning = samplesWarning; + renderSettingsStorage.renderSettingsPrefabWarning = prefabWarning; + renderSettingsStorage.renderSettingsSplitByScene = ftBuildGraphics.splitByScene; + renderSettingsStorage.renderSettingsExportTerrainAsHeightmap = ftBuildGraphics.exportTerrainAsHeightmap; + renderSettingsStorage.renderSettingsExportTerrainTrees = ftBuildGraphics.exportTerrainTrees; + renderSettingsStorage.renderSettingsRTXMode = rtxMode; + renderSettingsStorage.renderSettingsLightProbeMode = (int)lightProbeMode; + renderSettingsStorage.renderSettingsServerAddress = ftClient.serverAddress; + renderSettingsStorage.renderSettingsClientMode = clientMode; + renderSettingsStorage.renderSettingsUVPaddingMax = ftBuildGraphics.uvPaddingMax; + renderSettingsStorage.renderSettingsPostPacking = ftBuildGraphics.postPacking; + renderSettingsStorage.renderSettingsHoleFilling = ftBuildGraphics.holeFilling; + renderSettingsStorage.renderSettingsSampleDiv = sampleDivisor; + renderSettingsStorage.renderSettingsUnwrapper = (int)unwrapper; + renderSettingsStorage.renderSettingsDenoiserType = (int)denoiserType; + //renderSettingsStorage.renderSettingsLegacyDenoiser = (denoiserType == ftGlobalStorage.DenoiserType.Optix5);//legacyDenoiser; + renderSettingsStorage.renderSettingsAtlasPacker = ftBuildGraphics.atlasPacker; + renderSettingsStorage.renderSettingsCompressVolumes = compressVolumes; + renderSettingsStorage.renderSettingsSector = curSector; + } + } + + void RenderLightProbesUpdate() + { + if (!progressFunc.MoveNext()) + { + EditorApplication.update -= RenderLightProbesUpdate; + } + + } + + void RenderReflProbesUpdate() + { + if (!progressFunc.MoveNext()) + { + EditorApplication.update -= RenderReflProbesUpdate; + } + + } + + static float AreaElement(float x, float y) + { + return Mathf.Atan2(x * y, Mathf.Sqrt(x * x + y * y + 1)); + } + + const float inv2SqrtPI = 0.28209479177387814347403972578039f; // 1.0f / (2.0f * Mathf.Sqrt(Mathf.PI)) + const float sqrt3Div2SqrtPI = 0.48860251190291992158638462283835f; // Mathf.Sqrt(3.0f) / (2.0f * Mathf.Sqrt(Mathf.PI)) + const float sqrt15Div2SqrtPI = 1.0925484305920790705433857058027f; // Mathf.Sqrt(15.0f) / (2 * Mathf.Sqrt(Mathf.PI)) + const float threeSqrt5Div4SqrtPI = 0.94617469575756001809268107088713f; // 3 * Mathf.Sqrt(5.0f) / (4*Mathf.Sqrt(Mathf.PI)) + const float sqrt15Div4SqrtPI = 0.54627421529603953527169285290135f; // Mathf.Sqrt(15.0f) / (4 * Mathf.Sqrt(Mathf.PI)) + const float oneThird = 1.0f / 3.0f; + + static void EvalSHBasis9(Vector3 dir, ref float[] basis) + { + float dx = -dir.x; + float dy = -dir.y; + float dz = dir.z; + basis[0] = inv2SqrtPI * ftAdditionalConfig.irradianceConvolutionL0; + basis[1] = - dy * sqrt3Div2SqrtPI * ftAdditionalConfig.irradianceConvolutionL1; + basis[2] = dz * sqrt3Div2SqrtPI * ftAdditionalConfig.irradianceConvolutionL1; + basis[3] = - dx * sqrt3Div2SqrtPI * ftAdditionalConfig.irradianceConvolutionL1; + basis[4] = dx * dy * sqrt15Div2SqrtPI * ftAdditionalConfig.irradianceConvolutionL2_4_5_7; + basis[5] = - dy * dz * sqrt15Div2SqrtPI * ftAdditionalConfig.irradianceConvolutionL2_4_5_7; + basis[6] = (dz*dz-oneThird) * threeSqrt5Div4SqrtPI * ftAdditionalConfig.irradianceConvolutionL2_6; + basis[7] = - dx * dz * sqrt15Div2SqrtPI * ftAdditionalConfig.irradianceConvolutionL2_4_5_7; + basis[8] = (dx*dx-dy*dy) * sqrt15Div4SqrtPI * ftAdditionalConfig.irradianceConvolutionL2_8; + } + + public static BakeryVolume[] FindBakeableVolumes() + { + var vols = FindObjectsOfType(); + var vols2 = new List(); + Transform sectorTform = null; + if (curSector != null) sectorTform = curSector.transform; + for(int v=0; v 1.0f) a = 1.0f; + a = Mathf.Ceil(a * 255.0f) / 255.0f; + float invA = 1.0f / a; + l0r *= invA; + l0g *= invA; + l0b *= invA; + texData0[index] = new Color(l0r, l0g, l0b, a); + #else + texData0[index] = new Color(l0r, l0g, l0b, 1.0f); + #endif + texData1[index] = new Color(l1xr, l1xg, l1xb, 1.0f); + texData2[index] = new Color(l1yr, l1yg, l1yb, 1.0f); + texData3[index] = new Color(l1zr, l1zg, l1zb, 1.0f); + if (index == lastProbeInSlice) + { + // Would be nice if CompressTexture had separate src/dest args and we could reuse the textures... + texSliceHDR = new Texture2D(rx, ry, uncompressedHDRFormat, false); + + // L0 + texSliceHDR.SetPixels(texData0); + texSliceHDR.Apply(); + EditorUtility.CompressTexture(texSliceHDR, compressedHDRFormat, +#if UNITY_2019_3_OR_NEWER + UnityEditor.TextureCompressionQuality.Best); +#else + (int)UnityEngine.TextureCompressionQuality.Best); +#endif + var sliceBytes = texSliceHDR.GetRawTextureData(); + if (compressedSliceSizeHDR == 0) + { + compressedTexData0 = new byte[sliceBytes.Length * rz]; + compressedSliceSizeHDR = sliceBytes.Length; + } + int coffset = compressedSliceSizeHDR * z; + for(int c=0; c(path); + if (dest == null) + { + AssetDatabase.CreateAsset(src, path); + dest = src; + } + else + { + EditorUtility.CopySerialized(src, dest); + EditorUtility.SetDirty(dest); + } + return dest; + } + + public static void RestoreSceneManagerSetup(SceneSetup[] sceneSetups) + { + EditorSceneManager.RestoreSceneManagerSetup(sceneSetups); + } + + static public void DebugLogError(string text) + { + userCanceled = true; + ProgressBarEnd(); + if (verbose) + { + EditorUtility.DisplayDialog("Bakery error", text, "OK"); + } + else + { + Debug.LogError(text); + } + } + + public static void DebugLogInfo(string info) + { + if (pstorage == null) pstorage = ftLightmaps.GetProjectSettings(); + if ((pstorage.logLevel & (int)BakeryProjectSettings.LogLevel.Info) != 0) Debug.Log(info); + } + + public static void DebugLogWarning(string info) + { + if (pstorage == null) pstorage = ftLightmaps.GetProjectSettings(); + if ((pstorage.logLevel & (int)BakeryProjectSettings.LogLevel.Warning) != 0) Debug.LogWarning(info); + } + + IEnumerator RenderReflProbesFunc() + { + ProgressBarInit("Rendering reflection probes..."); + + // Put empty lighting data asset to scenes to prevent reflection probes bake trying to re-render everything + int sceneCount = SceneManager.sceneCount; + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + for(int s=0; s(); + ulht2.type = light.type; + ulht2.lightmapBakeType = LightmapBakeType.Mixed; + ulht2.shadows = LightShadows.Soft; + ulht2.range = light.range; + ulht2.transform.position = light.transform.position; + GameObjectUtility.SetStaticEditorFlags(g, StaticEditorFlags.LightmapStatic); + return ulht2; + } + + bool GetLightDataForPatching(Light lightTemp, Light lightReal, ref Dictionary idMap, ref Dictionary realID2Channel) + { + if (inspectorModeInfo == null) + inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance); + + var so = new SerializedObject(lightReal); + inspectorModeInfo.SetValue(so, InspectorMode.Debug, null); + long realID = so.FindProperty("m_LocalIdentfierInFile").longValue; + realID2Channel[realID] = so.FindProperty("m_BakingOutput").FindPropertyRelative("occlusionMaskChannel").intValue; + + so = new SerializedObject(lightTemp); + inspectorModeInfo.SetValue(so, InspectorMode.Debug, null); + long tempID = so.FindProperty("m_LocalIdentfierInFile").longValue; + + if (tempID == 0) + { + DebugLogError("tempID == 0"); + return false; + } + + if (realID == 0) + { + DebugLogError("realID == 0"); + return false; + } + + idMap[tempID] = realID; + + return true; + } +#endif + + bool IsLightCompletelyBaked(bool bakeToIndirect, RenderMode rmode) + { + bool isBaked = ((rmode == RenderMode.FullLighting) || + (rmode == RenderMode.Indirect && bakeToIndirect) || + (rmode == RenderMode.Shadowmask && bakeToIndirect)); + return isBaked; + } + + void MarkLightAsCompletelyBaked(Light ulht) + { + var st = storages[ulht.gameObject.scene]; + if (!st.bakedLights.Contains(ulht)) + { + st.bakedLights.Add(ulht); + st.bakedLightChannels.Add(-1); + } + +#if UNITY_2017_3_OR_NEWER + var output = new LightBakingOutput(); + output.isBaked = true; + output.lightmapBakeType = LightmapBakeType.Baked; + ulht.bakingOutput = output; +#endif + } + + bool IsLightRealtime(bool bakeToIndirect, RenderMode rmode) + { + bool isRealtime = ((rmode == RenderMode.Indirect && !bakeToIndirect) || + (rmode == RenderMode.Shadowmask && !bakeToIndirect)); + return isRealtime; + } + + void MarkLightAsRealtime(Light ulht) + { +#if UNITY_2017_3_OR_NEWER + var output = new LightBakingOutput(); + output.isBaked = false; + output.lightmapBakeType = LightmapBakeType.Realtime; + output.mixedLightingMode = MixedLightingMode.IndirectOnly; + output.occlusionMaskChannel = -1; + output.probeOcclusionLightIndex = -1; + ulht.bakingOutput = output; +#endif + } + + bool IsLightSubtractive(bool bakeToIndirect, RenderMode rmode) + { + return rmode == RenderMode.Subtractive; + } + + void MarkLightAsSubtractive(Light ulht) + { + var st = storages[ulht.gameObject.scene]; + if (!st.bakedLights.Contains(ulht)) + { + st.bakedLights.Add(ulht); + st.bakedLightChannels.Add(101); + } + +#if UNITY_2017_3_OR_NEWER + var output = new LightBakingOutput(); + output.isBaked = true; + output.lightmapBakeType = LightmapBakeType.Mixed; + output.mixedLightingMode = MixedLightingMode.Subtractive; + output.occlusionMaskChannel = -1; + output.probeOcclusionLightIndex = -1; + ulht.bakingOutput = output; +#else + ulht.alreadyLightmapped = true; + ulht.lightmapBakeType = LightmapBakeType.Mixed; + var so = new SerializedObject(ulht); + var sp = so.FindProperty("m_BakingOutput"); + sp.FindPropertyRelative("occlusionMaskChannel").intValue = 0; + sp.FindPropertyRelative("lightmappingMask").intValue = 131076; + so.ApplyModifiedProperties(); + + if (!maskedLights.Contains(ulht)) maskedLights.Add(ulht); +#endif + } + + void SceneSavedTest(Scene scene) + { + if (sceneSavedTestScene == scene) sceneWasSaved = true; + } + + static int GetShadowmaskChannel(BakeryPointLight a) + { + int channelA = -1; + if (!a.shadowmask) return channelA; + var uA = a.GetComponent(); + if (uA != null) + { + var stA = storages[a.gameObject.scene]; + int indexA = stA.bakedLights.IndexOf(uA); + if (indexA >= 0 && indexA < stA.bakedLightChannels.Count) + { + channelA = stA.bakedLightChannels[indexA]; + } + } + return channelA; + } + + static int ComparePointLights(BakeryPointLight a, BakeryPointLight b) + { + int channelA = GetShadowmaskChannel(a); + float compA = channelA * 10000 + (a.bakeToIndirect ? 1000 : 0) + a.indirectIntensity; + + int channelB = GetShadowmaskChannel(b); + float compB = channelB * 10000 + (b.bakeToIndirect ? 1000 : 0) + b.indirectIntensity; + + return compB.CompareTo(compA); + } + + public IEnumerator InitializeLightProbes(bool optional) + { + hasAnyProbes = true; + var probeGroups = FindObjectsOfType(typeof(LightProbeGroup)) as LightProbeGroup[]; + if (probeGroups.Length == 0) + { + if (!optional) DebugLogError("Add at least one LightProbeGroup"); + hasAnyProbes = false; + yield break; + } + else + { + int totalProbes = 0; + for(int i=0; i(); + if (store == null) { + store = fgo.AddComponent(); + } + +#if UNITY_2017_2_OR_NEWER + if (LightmapEditorSettings.lightmapper == BUILTIN_RADIOSITY) + { + bool cont = true; + if (verbose) + { + cont = EditorUtility.DisplayDialog("Bakery", "Unity does not currently support external occlusion probes. You are going to generate them using Enlighten. This process can take an eternity of time. It is recommended to use Progressive to generate them instead.", "Use Progressive", "Continue anyway"); + } + else + { + Debug.LogError("Enlighten used to generate occlusion probes"); + } + if (cont) + { + LightmapEditorSettings.lightmapper = BUILTIN_PT; + } + } + else + { + if (!store.enlightenWarningShown) + { + if (verbose) + { + if (!EditorUtility.DisplayDialog("Bakery", "Unity does not currently support external occlusion probes. You are going to generate them using Progressive.\n", "Continue anyway", "Cancel")) + { + hasAnyProbes = false; + yield break; + } + } + else + { + Debug.LogError("Enlighten used to generate occlusion probes"); + } + } + } + if (!store.enlightenWarningShown) + { + store.enlightenWarningShown = true; + EditorUtility.SetDirty(store); + } +#else + if (!store.enlightenWarningShown) + { + if (verbose) + { + if (!EditorUtility.DisplayDialog("Bakery", "Unity does not currently support external occlusion probes. You are going to generate them using Enlighten or Progressive - whichever is enabled in the Lighting window.\nMake sure you have selected Progressive, as Enlighten can take an eternity of time.", "Continue anyway", "Cancel")) + { + hasAnyProbes = false; + yield break; + } + store.enlightenWarningShown = true; + EditorUtility.SetDirty(store); + } + else + { + Debug.LogError("Enlighten used to generate occlusion probes"); + } + } +#endif + + var staticObjects = new List(); + var staticObjectsTerrain = new List(); + var staticObjectsScale = new List(); + var staticObjectsScaleTerrain = new List(); + try + { + // Temporarily zero scale in lightmap to prevent Unity from generating its lightmaps + // terrains? + var objs = Resources.FindObjectsOfTypeAll(typeof(GameObject)); + foreach(GameObject obj in objs) + { + if (obj == null) continue; + if (!obj.activeInHierarchy) continue; + var path = AssetDatabase.GetAssetPath(obj); + if (path != "") continue; // must belond to scene + //if ((obj.hideFlags & (HideFlags.DontSave|HideFlags.HideAndDontSave)) != 0) continue; // skip temp objects + //if (obj.tag == "EditorOnly") continue; // skip temp objects + //var areaLight = obj.GetComponent(); + //if (areaLight != null && !areaLight.selfShadow) continue; + var mr = ftBuildGraphics.GetValidRenderer(obj); + var mf = obj.GetComponent(); + var tr = obj.GetComponent(); + //if (((GameObjectUtility.GetStaticEditorFlags(obj) & StaticEditorFlags.LightmapStatic) == 0) && areaLight==null) continue; // skip dynamic + if ((GameObjectUtility.GetStaticEditorFlags(obj) & StaticEditorFlags.LightmapStatic) == 0) continue; // skip dynamic + + var sharedMesh = ftBuildGraphics.GetSharedMesh(mr); + + if (mr != null && mr.enabled && mf != null && sharedMesh != null) + { + var so = new SerializedObject(mr); + var prop = so.FindProperty("m_ScaleInLightmap"); + var scaleInLm = prop.floatValue; + if (scaleInLm == 0) continue; + staticObjectsScale.Add(scaleInLm); + prop.floatValue = 0; + so.ApplyModifiedProperties(); + staticObjects.Add(mr); + } + + if (tr != null && tr.enabled) + { + var so = new SerializedObject(tr); + var prop = so.FindProperty("m_ScaleInLightmap"); + var scaleInLm = prop.floatValue; + if (scaleInLm == 0) continue; + staticObjectsScaleTerrain.Add(scaleInLm); + prop.floatValue = 0; + so.ApplyModifiedProperties(); + staticObjectsTerrain.Add(tr); + } + } + } + catch + { + Debug.LogError("Failed rendering light probes"); + throw; + } + + var lms = LightmapSettings.lightmaps; + Texture2D firstLM = null; + if (lms.Length > 0) firstLM = lms[0].lightmapColor; + + Lightmapping.BakeAsync(); + ProgressBarInit("Waiting for Unity to initialize the probes..."); + while(Lightmapping.isRunning) + { + userCanceled = simpleProgressBarCancelled(); + if (userCanceled) + { + Lightmapping.Cancel(); + ProgressBarEnd(); + break; + } + yield return null; + } + ProgressBarEnd(); + + lms = LightmapSettings.lightmaps; + if (lms.Length == 1 && lms[0].lightmapColor != firstLM) + { + // During occlusion probe rendering Unity also generated useless tiny LMs - delete them to prevent lightmap array pollution + if (lms[0].lightmapColor != null) AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(lms[0].lightmapColor)); + if (lms[0].lightmapDir != null) AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(lms[0].lightmapDir)); + } + + for(int i=0; i tempID2RealID = null; + Dictionary realID2Channel = null; +#endif + + reflProbesValue = QualitySettings.realtimeReflectionProbes; + QualitySettings.realtimeReflectionProbes = true; + revertReflProbesValue = true; + + if (!useUnityForOcclsusionProbes) + { + setup = EditorSceneManager.GetSceneManagerSetup(); + } + + if (!useUnityForOcclsusionProbes) + { + if (verbose) + { + if (!EditorSceneManager.EnsureUntitledSceneHasBeenSaved("Please save all scenes before rendering")) + { + yield break; + } + } + else + { + EditorSceneManager.SaveOpenScenes(); + } + var assetName = GenerateLightingDataAssetName(); + + scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive); + SceneManager.SetActiveScene(scene); + RenderSettings.skybox = null; + LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional; + + var probeGroupClones = new GameObject[probeGroups.Length]; + for(int i=0; i(); + p.probePositions = probeGroups[i].probePositions; + SceneManager.MoveGameObjectToScene(g, scene); + probeGroupClones[i] = g; + } + +#if UNITY_2017_3_OR_NEWER +#else + // Make sure shadowmask lights are present in LightingDataAsset together with probes + // Occlusion channel needs to be patched later + List maskedLightsTemp = null; + List maskedLightsReal = null; + if (userRenderMode == RenderMode.Shadowmask || userRenderMode == RenderMode.Subtractive) + { + maskedLightsTemp = new List(); + maskedLightsReal = new List(); + AllP = FindObjectsOfType(typeof(BakeryPointLight)) as BakeryPointLight[]; + All3 = FindObjectsOfType(typeof(BakeryDirectLight)) as BakeryDirectLight[]; + for(int i=0; i(); + if (ulht == null) continue; + maskedLightsTemp.Add(AddTempShadowmaskLight(ulht, scene)); + maskedLightsReal.Add(ulht); + } + for(int i=0; i(); + if (ulht == null) continue; + maskedLightsTemp.Add(AddTempShadowmaskLight(ulht, scene)); + maskedLightsReal.Add(ulht); + } + } + //var tempQuad = GameObject.CreatePrimitive(PrimitiveType.Quad); + //SceneManager.MoveGameObjectToScene(tempQuad, scene); + //GameObjectUtility.SetStaticEditorFlags(tempQuad, StaticEditorFlags.LightmapStatic); + +#endif + + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + var tempScenePath = bakeryRuntimePath + "_tempScene.unity"; + sceneSavedTestScene = scene; + sceneWasSaved = false; + EditorSceneManager.sceneSaved += SceneSavedTest; + var saved = EditorSceneManager.SaveScene(scene, tempScenePath); + if (!saved) + { + hasAnyProbes = false; + DebugLogError("RenderLightProbes error: can't save temporary scene"); + RestoreSceneManagerSetup(setup); + yield break; + } + while(!sceneWasSaved) + { + yield return null; + } + EditorSceneManager.sceneSaved -= SceneSavedTest; + +#if UNITY_2017_3_OR_NEWER +#else + if (userRenderMode == RenderMode.Shadowmask || userRenderMode == RenderMode.Subtractive) + { + tempID2RealID = new Dictionary(); + realID2Channel = new Dictionary(); + for(int i=0; i= 0) + { + assetName = assetName.Substring(0, index); + } + else + { + assetName += "_copy"; + } + newPath = "Assets/" + outputPath + "/" + assetName + ".asset"; + locked = true; + } + if (!locked) ftest.Close(); + } + +#if UNITY_2017_3_OR_NEWER +#else + if (userRenderMode == RenderMode.Shadowmask || userRenderMode == RenderMode.Subtractive) + { + if (!useUnityForOcclsusionProbes) + { + if (!ftLightingDataGen.PatchShadowmaskLightingData(lmdataPath, newPath, ref tempID2RealID, ref realID2Channel, userRenderMode == RenderMode.Subtractive)) + { + try + { + File.Copy(lmdataPath, newPath, true); + } + catch + { + //success = false; + Debug.LogError("Failed copying LightingDataAsset"); + } + } + } + } + else + { +#endif + if (!useUnityForOcclsusionProbes) + { + //for(int i=0; i<3; i++) + //{ + //bool success = true; + try + { + File.Copy(lmdataPath, newPath, true); + } + catch + { + //success = false; + Debug.LogError("Failed copying LightingDataAsset"); + } + //if (success) break; + //yield return new WaitForSeconds(1); + //} + } +#if UNITY_2017_3_OR_NEWER +#else + } +#endif + + if (!useUnityForOcclsusionProbes) + { + AssetDatabase.Refresh(); + newAssetLData = ApplyLightingDataAsset(newPath); + EditorSceneManager.MarkAllScenesDirty(); + + EditorSceneManager.SaveOpenScenes(); + RestoreSceneManagerSetup(setup); + + //var sanityTimeout = GetTime() + 5; + while( (sceneCount > EditorSceneManager.sceneCount || EditorSceneManager.GetSceneAt(0).path.Length == 0))// && GetTime() < sanityTimeout ) + { + yield return null; + } + + LoadRenderSettings(); // prevent curSector reference from unloading + ftLightmaps.RefreshFull(); + } + } + + IEnumerator RenderLightProbesFunc() + { + int maxThreads = Mathf.Max(2, System.Environment.ProcessorCount * 2); + DebugLogInfo("Multi-threading to " + maxThreads + " threads."); + lightProbeRenderSize = 64; + lightProbeReadSize = 8; + var proc = InitializeLightProbes(false); + while (proc.MoveNext()) yield return null; + if (!hasAnyProbes) yield break; + + var activeScene = EditorSceneManager.GetActiveScene(); + + LightingDataAsset newAsset = newAssetLData; + List dynamicObjects = null; + GameObject[] go = new GameObject[maxThreads]; + ReflectionProbe[] probe = new ReflectionProbe[maxThreads]; + RenderTexture[] rt = new RenderTexture[maxThreads]; + Material mat = null; + Texture2D[] tex = new Texture2D[maxThreads]; + + Material origSkybox = RenderSettings.skybox; + Material tempSkybox; + string ftSkyboxShaderName = "Bakery/Skybox"; + + //if (!useUnityForLightProbes) + { + // Disable all dynamic objects + //var objects = UnityEngine.Object.FindObjectsOfTypeAll(typeof(GameObject)); + var objects = Resources.FindObjectsOfTypeAll(typeof(GameObject)); + dynamicObjects = new List(); + var dynAllowMask = forceProbeVisibility.value; + foreach (GameObject obj in objects) + { + if (!obj.activeInHierarchy) continue; + var path = AssetDatabase.GetAssetPath(obj); + if (path != "") continue; // must belond to scene + //if ((obj.hideFlags & (HideFlags.DontSave|HideFlags.HideAndDontSave)) != 0) continue; // skip temp objects + //if (obj.tag == "EditorOnly") continue; // skip temp objects + if ((GameObjectUtility.GetStaticEditorFlags(obj) & StaticEditorFlags.LightmapStatic) != 0) continue; // skip static + var mr = ftBuildGraphics.GetValidRenderer(obj); + if (mr == null) continue; // must have visible mesh + if (!mr.enabled) continue; // renderer must be on + if ((obj.layer & dynAllowMask) != 0) continue; // don't hide renderers with forceProbeVisibility mask + mr.enabled = false; + dynamicObjects.Add(mr); + } + + // Change skybox to first Skylight + var skyLights = FindObjectsOfType(typeof(BakerySkyLight)) as BakerySkyLight[]; + BakerySkyLight firstSkyLight = skyLights.Length > 0 ? skyLights[0] : null; + tempSkybox = new Material(Shader.Find(ftSkyboxShaderName)); + if (firstSkyLight != null) + { + tempSkybox.SetTexture("_Tex", firstSkyLight.cubemap as Cubemap); + tempSkybox.SetFloat("_NoTexture", firstSkyLight.cubemap == null ? 1 : 0); + tempSkybox.SetFloat("_Hemispherical", firstSkyLight.hemispherical ? 1 : 0); + tempSkybox.SetFloat("_Exposure", firstSkyLight.intensity); + tempSkybox.SetColor("_Tint", PlayerSettings.colorSpace == ColorSpace.Linear ? firstSkyLight.color : firstSkyLight.color.linear); + tempSkybox.SetVector("_MatrixRight", firstSkyLight.transform.right); + tempSkybox.SetVector("_MatrixUp", firstSkyLight.transform.up); + tempSkybox.SetVector("_MatrixForward", firstSkyLight.transform.forward); + } + else + { + tempSkybox.SetFloat("_NoTexture", 1); + tempSkybox.SetColor("_Tint", Color.black); + } + RenderSettings.skybox = tempSkybox; + + for (int i = 0; i < maxThreads; i++) + { + go[i] = new GameObject(); + probe[i] = go[i].AddComponent() as ReflectionProbe; + probe[i].resolution = lightProbeRenderSize; + probe[i].hdr = true; + probe[i].refreshMode = ReflectionProbeRefreshMode.ViaScripting; + probe[i].timeSlicingMode = ReflectionProbeTimeSlicingMode.NoTimeSlicing; + probe[i].mode = ReflectionProbeMode.Realtime; + probe[i].intensity = 0; + probe[i].nearClipPlane = 0.0001f; // this isn't good but works so far + + rt[i] = new RenderTexture(lightProbeReadSize * 6, lightProbeReadSize, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear); + tex[i] = new Texture2D(lightProbeReadSize * 6, lightProbeReadSize, TextureFormat.RGBAFloat, false, true); + } + if (matCubemapToStrip == null) matCubemapToStrip = new Material(Shader.Find("Hidden/ftCubemap2Strip")); + mat = matCubemapToStrip; + } + + var directions = new Vector3[lightProbeReadSize * lightProbeReadSize]; + var solidAngles = new float[lightProbeReadSize * lightProbeReadSize]; + float readTexelSize = 1.0f / lightProbeReadSize; + float weightAccum = 0; + for (int y = 0; y < lightProbeReadSize; y++) + { + for (int x = 0; x < lightProbeReadSize; x++) + { + float u = (x / (float)(lightProbeReadSize - 1)) * 2 - 1; + float v = (y / (float)(lightProbeReadSize - 1)) * 2 - 1; + directions[y * lightProbeReadSize + x] = (new Vector3(u, v, 1.0f)).normalized; + + + float x0 = u - readTexelSize; + float y0 = v - readTexelSize; + float x1 = u + readTexelSize; + float y1 = v + readTexelSize; + float solidAngle = AreaElement(x0, y0) - AreaElement(x0, y1) - AreaElement(x1, y0) + AreaElement(x1, y1); + weightAccum += solidAngle; + solidAngles[y * lightProbeReadSize + x] = solidAngle; + } + } + weightAccum *= 6; + weightAccum *= Mathf.PI; + + var probes = LightmapSettings.lightProbes; + if (probes == null) + { + DebugLogError("RenderLightProbes error: no probes in LightingDataAsset"); + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + //RestoreSceneManagerSetup(setup); + foreach (GameObject g in go) DestroyImmediate(g); + //userCanceled = true; + //ProgressBarEnd(); + bakeInProgress = false; + yield break; + } + SphericalHarmonicsL2[] shs; + //if (!useUnityForLightProbes) + { + shs = new SphericalHarmonicsL2[probes.count]; + } + //else + { + //shs = probes.bakedProbes; + } + + var positions = probes.positions; + + var directLights = FindObjectsOfType(typeof(BakeryDirectLight)) as BakeryDirectLight[]; + var pointLights = FindObjectsOfType(typeof(BakeryPointLight)) as BakeryPointLight[]; + + if (userRenderMode == RenderMode.Indirect || userRenderMode == RenderMode.Shadowmask) + { + var filteredDirectLights = new List(); + var filteredPointLights = new List(); + for (int i = 0; i < directLights.Length; i++) if (directLights[i].enabled && directLights[i].bakeToIndirect) filteredDirectLights.Add(directLights[i]); + for (int i = 0; i < pointLights.Length; i++) if (pointLights[i].enabled && pointLights[i].bakeToIndirect) filteredPointLights.Add(pointLights[i]); + directLights = filteredDirectLights.ToArray(); + pointLights = filteredPointLights.ToArray(); + } + else + { + var filteredDirectLights = new List(); + var filteredPointLights = new List(); + for (int i = 0; i < directLights.Length; i++) if (directLights[i].enabled) filteredDirectLights.Add(directLights[i]); + for (int i = 0; i < pointLights.Length; i++) if (pointLights[i].enabled) filteredPointLights.Add(pointLights[i]); + directLights = filteredDirectLights.ToArray(); + pointLights = filteredPointLights.ToArray(); + } + + bool anyDirectLightToBake = (directLights.Length > 0 || pointLights.Length > 0);// && userRenderMode == RenderMode.FullLighting; + float[] uvpos = null; + byte[] uvnormal = null; + int atlasTexSize = 0; + List[] dirsPerProbe = new List[probes.count]; + List[] dirColorsPerProbe = new List[probes.count]; + if (anyDirectLightToBake) + { + atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)probes.count)); + atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)tileSize) * tileSize; + uvpos = new float[atlasTexSize * atlasTexSize * 4]; + uvnormal = new byte[atlasTexSize * atlasTexSize * 4]; + } + + userCanceled = false; + ProgressBarInit("Rendering lightprobes..."); + yield return null; + + ftBuildGraphics.CreateSceneFolder(); + + if (anyDirectLightToBake) + { + ProgressBarShow("Rendering lightprobes - direct...", 0, true); + if (userCanceled) + { + ProgressBarEnd(); + foreach (GameObject g in go) DestroyImmediate(g); + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + + for (int i = 0; i < probes.count; i++) + { + int x = i % atlasTexSize; + int y = i / atlasTexSize; + int index = y * atlasTexSize + x; + uvpos[index * 4] = positions[i].x; + uvpos[index * 4 + 1] = positions[i].y; + uvpos[index * 4 + 2] = positions[i].z; + uvpos[index * 4 + 3] = 1.0f; + uvnormal[index * 4 + 1] = 255; + uvnormal[index * 4 + 3] = 255; + } + + var fpos = new BinaryWriter(File.Open(scenePath + "/uvpos_probes.dds", FileMode.Create)); + fpos.Write(ftDDS.ddsHeaderFloat4); + var posbytes = new byte[uvpos.Length * 4]; + System.Buffer.BlockCopy(uvpos, 0, posbytes, 0, posbytes.Length); + fpos.Write(posbytes); + fpos.BaseStream.Seek(12, SeekOrigin.Begin); + fpos.Write(atlasTexSize); + fpos.Write(atlasTexSize); + fpos.Close(); + + var fnorm = new BinaryWriter(File.Open(scenePath + "/uvnormal_probes.dds", FileMode.Create)); + fnorm.Write(ftDDS.ddsHeaderRGBA8); + fnorm.Write(uvnormal); + fnorm.BaseStream.Seek(12, SeekOrigin.Begin); + fnorm.Write(atlasTexSize); + fnorm.Write(atlasTexSize); + fnorm.Close(); + + if (!ftInitialized) + { + ftInitialized = true; + ftSceneDirty = true; + } + if (forceRebuildGeometry) + { + ftBuildGraphics.modifyLightmapStorage = false; + ftBuildGraphics.forceAllAreaLightsSelfshadow = false; + ftBuildGraphics.validateLightmapStorageImmutability = false; + var exportSceneFunc = ftBuildGraphics.ExportScene((ftRenderLightmap)EditorWindow.GetWindow(typeof(ftRenderLightmap)), false); + progressBarEnabled = true; + while (exportSceneFunc.MoveNext()) + { + progressBarText = ftBuildGraphics.progressBarText; + progressBarPercent = ftBuildGraphics.progressBarPercent; + if (ftBuildGraphics.userCanceled) + { + ProgressBarEnd(); + foreach (GameObject g in go) DestroyImmediate(g); + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + yield return null; + } + ftSceneDirty = true; + if (ftBuildGraphics.userCanceled) + { + userCanceled = ftBuildGraphics.userCanceled; + ProgressBarEnd(); + foreach (GameObject g in go) DestroyImmediate(g); + foreach (var d in dynamicObjects) + { + if (d != null) d.enabled = true; + } + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + EditorSceneManager.MarkAllScenesDirty(); + } + else + { + ValidateCurrentScene(); + } + + ftLightmaps.RefreshFull(); + + CollectStorages(); + var sceneCount = SceneManager.sceneCount; + for (int s = 0; s < sceneCount; s++) + { + var scene = EditorSceneManager.GetSceneAt(s); + if (!scene.isLoaded) continue; + storage = storages[scene]; + + // Clear temp data from storage + storage.uvBuffOffsets = new int[0]; + storage.uvBuffLengths = new int[0]; + storage.uvSrcBuff = new float[0]; + storage.uvDestBuff = new float[0]; + storage.lmrIndicesOffsets = new int[0]; + storage.lmrIndicesLengths = new int[0]; + storage.lmrIndicesBuff = new int[0]; + + storage.lmGroupLODResFlags = new int[0]; + storage.lmGroupMinLOD = new int[0]; + storage.lmGroupLODMatrix = new int[0]; + } + + + int LMID = 0; + var flms = new BinaryWriter(File.Open(scenePath + "/lms.bin", FileMode.Create)); + flms.Write("probes"); + flms.Write(atlasTexSize); + flms.Close(); + + var flmlod = new BinaryWriter(File.Open(scenePath + "/lmlod.bin", FileMode.Create)); + flmlod.Write(ftBuildGraphics.sceneLodsUsed > 0 ? 0 : -1); + flmlod.Close(); + + var fsettings = new BinaryWriter(File.Open(scenePath + "/settings.bin", FileMode.Create)); + fsettings.Write(tileSize); + fsettings.Write(false); + fsettings.Write(false); + fsettings.Write(deviceMask); + fsettings.Close(); + + int errCode = 0; + for (int i = 0; i < directLights.Length; i++) + { + ProgressBarShow("Rendering lightprobes - direct...", i / (float)(directLights.Length + pointLights.Length), true); + if (userCanceled) + { + ProgressBarEnd(); + foreach (GameObject g in go) DestroyImmediate(g); + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + yield return null; + + var light = directLights[i] as BakeryDirectLight; + ftBuildLights.BuildDirectLight(light, SampleCount(light.samples), true); + + if (exeMode) + { + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.Arguments = GetSunRenderMode(light) + " " + scenePathQuoted + " probes.dds " + PASS_HALF + " " + 0 + " " + LMID; + DebugLogInfo("Running ftrace " + startInfo.Arguments); +#if LAUNCH_VIA_DLL + var crt = ProcessCoroutine(ftraceExe, startInfo.Arguments); + while (crt.MoveNext()) + { + if (userCanceled) + { + ProgressBarEnd(); + foreach (GameObject g in go) DestroyImmediate(g); + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + yield return null; + } + if (userCanceled) + { + ProgressBarEnd(); + foreach (GameObject g in go) DestroyImmediate(g); + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + errCode = lastReturnValue; +#else + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + startInfo.WorkingDirectory = dllPath + "/Bakery"; + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + var exeProcess = System.Diagnostics.Process.Start(startInfo); + exeProcess.WaitForExit(); + errCode = exeProcess.ExitCode; +#endif + } + + if (errCode != 0) + { + DebugLogError("ftrace error: " + ftErrorCodes.TranslateFtrace(errCode, rtxMode)); + userCanceled = true; + foreach (GameObject g in go) DestroyImmediate(g); + + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + + var halfs = new ushort[atlasTexSize * atlasTexSize * 4]; + var halfBytes = new byte[halfs.Length * 2]; + var fprobes = new BinaryReader(File.Open(scenePath + "/probes.dds", FileMode.Open, FileAccess.Read)); + fprobes.BaseStream.Seek(128, SeekOrigin.Begin); + halfBytes = fprobes.ReadBytes(halfBytes.Length); + System.Buffer.BlockCopy(halfBytes, 0, halfs, 0, halfBytes.Length); + fprobes.Close(); + + var dir = light.transform.forward; + float cr = 0.0f; + float cg = 0.0f; + float cb = 0.0f; + for (int p = 0; p < probes.count; p++) + { + cr = Mathf.HalfToFloat(halfs[p * 4]); + cg = Mathf.HalfToFloat(halfs[p * 4 + 1]); + cb = Mathf.HalfToFloat(halfs[p * 4 + 2]); + if (cr + cg + cb <= 0) continue; + + if (dirsPerProbe[p] == null) + { + dirsPerProbe[p] = new List(); + dirColorsPerProbe[p] = new List(); + } + dirsPerProbe[p].Add(dir); + dirColorsPerProbe[p].Add(new Vector3(cr, cg, cb)); + } + } + + for (int i = 0; i < pointLights.Length; i++) + { + ProgressBarShow("Rendering lightprobes - direct...", (i + directLights.Length) / (float)(directLights.Length + pointLights.Length), true); + if (userCanceled) + { + ProgressBarEnd(); + foreach (GameObject g in go) DestroyImmediate(g); + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + yield return null; + + var light = pointLights[i] as BakeryPointLight; + bool isError = ftBuildLights.BuildLight(light, SampleCount(light.samples), true, true); // TODO: dirty tex detection!! + if (isError) + { + ProgressBarEnd(); + DebugLogError("BuildLight error"); + userCanceled = true; + foreach (GameObject g in go) DestroyImmediate(g); + + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + yield return null; + + string renderMode = GetPointLightRenderMode(light); + + if (exeMode) + { + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.Arguments = renderMode + " " + scenePathQuoted + " probes.dds " + PASS_HALF + " " + 0 + " " + LMID; + DebugLogInfo("Running ftrace " + startInfo.Arguments); +#if LAUNCH_VIA_DLL + var crt = ProcessCoroutine(ftraceExe, startInfo.Arguments); + while (crt.MoveNext()) + { + if (userCanceled) + { + ProgressBarEnd(); + foreach (GameObject g in go) DestroyImmediate(g); + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + yield return null; + } + if (userCanceled) + { + ProgressBarEnd(); + foreach (GameObject g in go) DestroyImmediate(g); + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + errCode = lastReturnValue; +#else + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + startInfo.WorkingDirectory = dllPath + "/Bakery"; + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + var exeProcess = System.Diagnostics.Process.Start(startInfo); + exeProcess.WaitForExit(); + errCode = exeProcess.ExitCode; +#endif + } + + if (errCode != 0) + { + ProgressBarEnd(); + DebugLogError("ftrace error: " + ftErrorCodes.TranslateFtrace(errCode, rtxMode)); + userCanceled = true; + foreach (GameObject g in go) DestroyImmediate(g); + + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + + var halfs = new ushort[atlasTexSize * atlasTexSize * 4]; + var halfBytes = new byte[halfs.Length * 2]; + var fprobes = new BinaryReader(File.Open(scenePath + "/probes.dds", FileMode.Open)); + fprobes.BaseStream.Seek(128, SeekOrigin.Begin); + halfBytes = fprobes.ReadBytes(halfBytes.Length); + System.Buffer.BlockCopy(halfBytes, 0, halfs, 0, halfBytes.Length); + fprobes.Close(); + + for (int p = 0; p < probes.count; p++) + { + var dir = (positions[p] - light.transform.position).normalized; + + float cr = Mathf.HalfToFloat(halfs[p * 4]); + float cg = Mathf.HalfToFloat(halfs[p * 4 + 1]); + float cb = Mathf.HalfToFloat(halfs[p * 4 + 2]); + if (cr + cg + cb <= 0) continue; + + if (dirsPerProbe[p] == null) + { + dirsPerProbe[p] = new List(); + dirColorsPerProbe[p] = new List(); + } + dirsPerProbe[p].Add(dir); + dirColorsPerProbe[p].Add(new Vector3(cr, cg, cb)); + } + } + } + + //float numPixels = lightProbeReadSize * lightProbeReadSize * 6; + + mat.SetFloat("gammaMode", PlayerSettings.colorSpace == ColorSpace.Linear ? 0 : 1); + + var eventArgs = new ProbeEventArgs(); + System.Threading.Thread[] thread = new System.Threading.Thread[maxThreads]; + + int currentThreadsCount = maxThreads; + int lastThreadsCount; + for (int i = 0; i < shs.Length + maxThreads; i = i + maxThreads) + { + lastThreadsCount = currentThreadsCount; + currentThreadsCount = Mathf.Min(shs.Length - i, maxThreads); + if (currentThreadsCount <= 0) { + if (i>0) for (int th = 0; th < lastThreadsCount; th++) thread[th].Join(); + break; + } + for (int ip = 0; ip < currentThreadsCount; ip++) + { + probe[ip].transform.position = positions[i + ip]; + } + + if (OnPreRenderProbe != null) + { + eventArgs.pos = positions[i]; + OnPreRenderProbe.Invoke(this, eventArgs); + } + + int[] handle = new int[currentThreadsCount]; + for (int ip = 0; ip < currentThreadsCount; ip++) + { + handle[ip] = probe[ip].RenderProbe(); + } + yield return null; + + for (int ip = 0; ip < currentThreadsCount; ip++) + { + while (!probe[ip].IsFinishedRendering(handle[ip])) + { + yield return null; + } + + var cubemap = probe[ip].texture as RenderTexture; + Graphics.Blit(cubemap, rt[ip], mat); + Graphics.SetRenderTarget(rt[ip]); + tex[ip].ReadPixels(new Rect(0, 0, lightProbeReadSize * 6, lightProbeReadSize), 0, 0, false); + tex[ip].Apply(); + } + + + + for (int ip = 0; ip < currentThreadsCount; ip++) + { + int ii = i + ip; + var bytes = tex[ip].GetRawTextureData(); + SphericalHarmonicsL2 sh; + sh = new SphericalHarmonicsL2(); + sh.Clear(); + + if (i > 0) for (int th = 0; th < lastThreadsCount; th++) thread[th].Join(); + + thread[ip] = new System.Threading.Thread(() => + { + float[] basis = new float[9]; + float[] pixels = new float[bytes.Length / 4]; + System.Buffer.BlockCopy(bytes, 0, pixels, 0, bytes.Length); + + var probeDirLights = dirsPerProbe[ii]; + var probeDirLightColors = dirColorsPerProbe[ii]; + + + for (int face = 0; face < 6; face++) + { + for (int y = 0; y < lightProbeReadSize; y++) + { + for (int x = 0; x < lightProbeReadSize; x++) + { + var dir = directions[y * lightProbeReadSize + x]; + //Vector3 dirL; + + var solidAngle = solidAngles[y * lightProbeReadSize + x]; + + float stx = x / (float)(lightProbeReadSize - 1); + stx = stx * 2 - 1; + float sty = y / (float)(lightProbeReadSize - 1); + sty = sty * 2 - 1; + if (face == 0) + { + dir = new Vector3(-1, -sty, stx); + } + else if (face == 1) + { + dir = new Vector3(1, -sty, -stx); + } + else if (face == 2) + { + dir = new Vector3(-sty, -1, -stx); + } + else if (face == 3) + { + dir = new Vector3(-sty, 1, stx); + } + else if (face == 4) + { + dir = new Vector3(-stx, -sty, -1); + } + else + { + dir = new Vector3(stx, -sty, 1); + } + dir = dir.normalized; + + float cr = 0.0f; + float cg = 0.0f; + float cb = 0.0f; + int pixelAddr = y * lightProbeReadSize * 6 + x + face * lightProbeReadSize; + cr = pixels[pixelAddr * 4]; + cg = pixels[pixelAddr * 4 + 1]; + cb = pixels[pixelAddr * 4 + 2]; + + if (cr + cg + cb > 0) + { + EvalSHBasis9(dir, ref basis); + for (int b = 0; b < 9; b++) + { + if (b == lightProbeMaxCoeffs) break; + + // solidAngle is a weight for texels to account for cube shape of the cubemap (we need sphere) + sh[0, b] += cr * basis[b] * solidAngle; + sh[1, b] += cg * basis[b] * solidAngle; + sh[2, b] += cb * basis[b] * solidAngle; + } + } + + } + } + } + + if (probeDirLights != null) + { + const float norm = 2.9567930857315701067858823529412f; + for (int d = 0; d < probeDirLights.Count; d++) + { + var clr = probeDirLightColors[d]; + EvalSHBasis9(-probeDirLights[d], ref basis); + for (int b = 0; b < 9; b++) + { + if (b == lightProbeMaxCoeffs) break; + sh[0, b] += clr.x * basis[b] * norm; + sh[1, b] += clr.y * basis[b] * norm; + sh[2, b] += clr.z * basis[b] * norm; + } + } + } + + shs[ii] = sh; + }); + + thread[ip].IsBackground = true; + thread[ip].Start(); + } + + ProgressBarShow("Rendering lightprobes - GI...", (i / (float)probes.count), true); + if (userCanceled) + { + ProgressBarEnd(); + foreach (GameObject g in go) DestroyImmediate(g); + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + bakeInProgress = false; + yield break; + } + yield return null; + + } + + foreach (GameObject g in go) DestroyImmediate(g); + foreach (var d in dynamicObjects) d.enabled = true; + RenderSettings.skybox = origSkybox; + if (newAsset != null) EditorUtility.SetDirty(newAsset); + + probes.bakedProbes = shs; + EditorUtility.SetDirty(probes); + + SceneManager.SetActiveScene(activeScene); + + if (OnFinishedProbes != null) + { + OnFinishedProbes.Invoke(this, null); + } + + ProgressBarEnd(); + + bakeInProgress = false; + DebugLogInfo("Finished rendering Light Probes."); + yield break; + } + + + void RenderLightmapUpdate() + { + if (!exeMode) + { + while(progressFunc.MoveNext()) {} + EditorApplication.update -= RenderLightmapUpdate; + bakeInProgress = false; + } + else + { + if (!progressFunc.MoveNext()) + { + EditorApplication.update -= RenderLightmapUpdate; + bakeInProgress = false; + } + } + } + + int SetupLightShadowmaskUsingBitmask(Light ulht, int bitmask, int shadowmaskGroupID, int[] channelBitsPerLayer) + { + int foundChannel = -1; + if (shadowmaskGroupID > 0) + { + shadowmaskGroupIDToChannel.TryGetValue(shadowmaskGroupID, out foundChannel); + } + + if (foundChannel < 0) + { + // Find common available channels in affected layers + const int fourBits = 1|2|4|8; + int commonFreeBits = 0; + for(int layer=0; layer<32; layer++) + { + if ((bitmask & (1< 0) + { + shadowmaskGroupIDToChannel[shadowmaskGroupID] = foundChannel; + } + + return foundChannel; + } + + static void CollectStorages() + { + var sceneCount = SceneManager.sceneCount; + storages = new Dictionary(); + for(int i=0; i(); + if (storage == null) { + storage = go.AddComponent(); + } + storages[scene] = storage; + } + } + + bool CollectGroups(List groupList, List groupListGIContributing, bool selected, bool probes=false) + { + // 1: Collect + var sceneCount = SceneManager.sceneCount; + var groups = new List(); + + // Find explicit LMGroups + var groupsSelectors = FindObjectsOfType(typeof(BakeryLightmapGroupSelector)) as BakeryLightmapGroupSelector[]; + for(int i=0; i(); + groupListGIContributingPlain = new List(); + Object[] selObjs = null; + if (selected) + { + // Selected only + selObjs = Selection.objects; + if (selObjs.Length == 0) + { + DebugLogError("No objects selected"); + ProgressBarEnd(); + return false; + } + for(int o=0; o 64 && bounces > 0) + { + var warn = "GI uses more than 64 samples."; + if (warnCount < warnLimit) warns += warn + "\n"; + DebugLogWarning(warn); + warnCount++; + } + + if (hackAOSamples > 64 && hackAOIntensity > 0) + { + var warn = "AO uses more than 64 samples."; + if (warnCount < warnLimit) warns += warn + "\n"; + DebugLogWarning(warn); + warnCount++; + } + + for(int i=0; i 64 && All[i].selfShadow) + { + var warn = "Light " + All[i].name + " uses more than 64 near samples."; + if (warnCount < warnLimit) warns += warn + "\n"; + DebugLogWarning(warn); + warnCount++; + } + if (All[i].samples > 4096) + { + var warn = "Light " + All[i].name + " uses more than 4096 far samples."; + if (warnCount < warnLimit) warns += warn + "\n"; + DebugLogWarning(warn); + warnCount++; + } + } + for(int i=0; i 4096) + { + var warn = "Light " + AllP[i].name + " uses more than 4096 samples."; + if (warnCount < warnLimit) warns += warn + "\n"; + DebugLogWarning(warn); + warnCount++; + } + } + for(int i=0; i 64) + { + var warn = "Light " + All2[i].name + " uses more than 64 samples."; + if (warnCount < warnLimit) warns += warn + "\n"; + DebugLogWarning(warn); + warnCount++; + } + } + for(int i=0; i 64) + { + var warn = "Light " + All3[i].name + " uses more than 64 samples."; + if (warnCount < warnLimit) warns += warn + "\n"; + DebugLogWarning(warn); + warnCount++; + } + } + if (warnCount > 0) + { + if (verbose) + { + var warnText = "Some sample count values might be out of reasonable range. Extremely high values may cause GPU go out of available resources. This validation can be disabled.\n\n"; + warnText += warns; + if (warnCount >= warnLimit) warnText += "(See more warnings in console)"; + if (!EditorUtility.DisplayDialog("Bakery", warnText, "Continue", "Cancel")) + { + return false; + } + } + else + { + Debug.LogError("Some sample count values might be out of reasonable range"); + } + } + return true; + } + + bool ValidatePrefabs() + { + var lmprefabs2 = FindObjectsOfType(typeof(BakeryLightmappedPrefab)) as BakeryLightmappedPrefab[]; + var lmprefabsList = new List(); + int pwarnCount = 0; + int pwarnLimit = 32; + string pwarns = ""; + string pwarns2 = ""; + for(int i=0; i 0) + { + string warnText = ""; + if (pwarns2.Length > 0) + { + warnText += "These prefabs are going to be overwritten:\n\n" + pwarns2; + } + if (pwarns.Length > 0) + { + if (pwarns2.Length > 0) warnText += "\n\n"; + warnText += "These prefabs have baking enabled, but NOT going to be overwritten:\n\n" + pwarns; + } + if (warnText.Length > 0) + { + if (verbose) + { + if (!EditorUtility.DisplayDialog("Bakery", warnText, "Continue", "Cancel")) + { + return false; + } + } + else + { + Debug.LogError(warnText); + } + } + } + return true; + } + + IEnumerator RenderLightmapFunc() + { + // Basic validation + if (userRenderMode == RenderMode.Indirect && bounces < 1) + { + DebugLogError("Can't render indirect lightmaps, if bounces < 1"); + yield break; + } + + if (userRenderMode == RenderMode.AmbientOcclusionOnly) + { + if (hackAOIntensity <= 0 || hackAOSamples <= 0) + { + DebugLogError("AO intensity and samples must be > 0 to render AO-only map"); + yield break; + } + + if (renderDirMode != RenderDirMode.None && renderDirMode != RenderDirMode.DominantDirection) + { + DebugLogError("AO-only mode does not support RNM or SH."); + yield break; + } + } + + if (!exeMode && userRenderMode == RenderMode.Indirect) + { + DebugLogError("Selective baked direct lighting is not implemented in DLL mode"); + yield break; + } + + if (verbose) + { + if (!EditorSceneManager.EnsureUntitledSceneHasBeenSaved("Please save all scenes before rendering")) + { + yield break; + } + } + else + { + EditorSceneManager.SaveOpenScenes(); + } + + // Init probes + if (lightProbeMode == LightProbeMode.L1 && !selectedOnly && !fullSectorRender) + { + var proc = InitializeLightProbes(!probesOnlyL1); + while(proc.MoveNext()) yield return null; + if (probesOnlyL1 && !hasAnyProbes) yield break; + } + + // Alloc new data + if (clientMode) + { + ftClient.serverFileList = new List(); + ftClient.serverGetFileList = new List(); + } + + // Get base scene data + var activeScene = EditorSceneManager.GetActiveScene(); + var sceneCount = SceneManager.sceneCount; + + All = FindObjectsOfType(typeof(BakeryLightMesh)) as BakeryLightMesh[]; + AllP = FindObjectsOfType(typeof(BakeryPointLight)) as BakeryPointLight[]; + All2 = FindObjectsOfType(typeof(BakerySkyLight)) as BakerySkyLight[]; + All3 = FindObjectsOfType(typeof(BakeryDirectLight)) as BakeryDirectLight[]; + + // Scene data validation + if (samplesWarning) + { + if (!ValidateSamples()) yield break; + } + if (prefabWarning) + { + if (!ValidatePrefabs()) yield break; + } + + var sectors = FindObjectsOfType(typeof(BakerySector)) as BakerySector[]; + + // Unused (yet?) + if (!ftInitialized) + { + ftInitialized = true; + ftSceneDirty = true; + } + + // Create output dir + var outDir = Application.dataPath + "/" + outputPathFull; + if (!Directory.Exists(outDir)) Directory.CreateDirectory(outDir); + + // Init storages + storages = new Dictionary(); + for(int i=0; i(); + if (storage == null) { + storage = go.AddComponent(); + } + + // delete unused sectors from storages + var stSectors = storage.sectors; + if (stSectors == null) stSectors = storage.sectors = new List(); + + var newStSectors = new List(); + ftLightmapsStorage.SectorData globalSector = null; + for(int st=0; st(); + storage.masks = new List(); + storage.dirMaps = new List(); + storage.rnmMaps0 = new List(); + storage.rnmMaps1 = new List(); + storage.rnmMaps2 = new List(); + storage.mapsMode = new List(); + storage.bakedLights = new List(); + storage.bakedLightChannels = new List(); + storage.compressedVolumes = false; + storage.anyVolumes = false; + + //if (forceRefresh) // removed condition to make "Export" option work in isolation + { + storage.serverGetFileList = new List(); + storage.lightmapHasColor = new List(); + storage.lightmapHasMask = new List(); + storage.lightmapHasDir = new List(); + storage.lightmapHasRNM = new List(); + } + + storage.Init(forceRefresh); + + //ftBuildGraphics.storage = storage; + storages[scene] = storage; + } + SceneManager.SetActiveScene(activeScene); + + // Prepare realtime GI if needed + if (usesRealtimeGI && !probesOnlyL1) + { + var store = storages[activeScene]; +#if UNITY_2017_2_OR_NEWER + if (LightmapEditorSettings.lightmapper != BUILTIN_RADIOSITY) + { + if (verbose) + { + EditorUtility.DisplayDialog("Bakery", "'Combine with Enlighten real-time GI' is enabled, but Unity lightmapper is not set to Enlighten. Please go to Lighting settings and select it.", "OK"); + yield break; + } + else + { + Debug.LogError("'Combine with Enlighten real-time GI' is enabled, but Unity lightmapper is not set to Enlighten"); + } + } +#else + if (!store.enlightenWarningShown2) + { + if (verbose) + { + if (!EditorUtility.DisplayDialog("Bakery", "'Combine with Enlighten real-time GI' is enabled. Make sure Unity lightmapper is set to Enlighten in the Lighting window.", "I'm sure", "Cancel")) + { + yield break; + } + store.enlightenWarningShown2 = true; + EditorUtility.SetDirty(store); + } + else + { + Debug.LogError("'Combine with Enlighten real-time GI' is enabled, but Unity lightmapper is not set to Enlighten"); + } + } +#endif + + reflectionProbes = new List(); + + //Disable Refl probes, and Baked GI so all that we bake is Realtime GI + Lightmapping.bakedGI = false; + Lightmapping.realtimeGI = true; + FindAllReflectionProbesAndDisable(); + + //Bake to get the Realtime GI maps + //Lightmapping.Bake(); + + Lightmapping.BakeAsync(); + ProgressBarInit("Waiting for Enlighten..."); + while(Lightmapping.isRunning) + { + userCanceled = simpleProgressBarCancelled(); + if (userCanceled) + { + Lightmapping.Cancel(); + ProgressBarEnd(); + break; + } + yield return null; + } + ProgressBarEnd(); + + //Re enable probes before bakery bakes, and bakedGI + Lightmapping.bakedGI = true; + ReEnableReflectionProbes(); + } + + // Export scene + if (forceRebuildGeometry) + { + passedFilterFlag++; + + renderSettingsStorage = FindRenderSettingsStorage(); + SaveRenderSettings(); + + ftBuildGraphics.overwriteWarningSelectedOnly = selectedOnly; + ftBuildGraphics.modifyLightmapStorage = true; + ftBuildGraphics.forceAllAreaLightsSelfshadow = false; + ftBuildGraphics.validateLightmapStorageImmutability = selectedOnly || probesOnlyL1; + ftBuildGraphics.sceneNeedsToBeRebuilt = false; + var exportSceneFunc = ftBuildGraphics.ExportScene((ftRenderLightmap)EditorWindow.GetWindow(typeof(ftRenderLightmap)), true); + progressBarEnabled = true; + + var estartMs = GetTimeMs(); + while(exportSceneFunc.MoveNext()) + { + progressBarText = ftBuildGraphics.progressBarText; + progressBarPercent = ftBuildGraphics.progressBarPercent; + if (ftBuildGraphics.userCanceled) + { + ftBuildGraphics.ProgressBarEnd(true); + ProgressBarEnd(); + yield break; + } + yield return null; + } + + if (ftBuildGraphics.sceneNeedsToBeRebuilt) + { + ftBuildGraphics.ProgressBarEnd(true); + DebugLogError("Scene geometry/layout changed since last full bake. Use Render button instead."); + yield break; + } + + var ems = GetTimeMs(); + double exportTime = (ems - estartMs) / 1000.0; + DebugLogInfo("Scene export time: " + exportTime); + + userCanceled = ftBuildGraphics.userCanceled; + ProgressBarEnd(false); + ftSceneDirty = true; + if (ftBuildGraphics.userCanceled) yield break; + SaveRenderSettings(); + EditorSceneManager.MarkAllScenesDirty(); + } + else + { + if (!ValidateCurrentScene()) + { + ProgressBarEnd(); + yield break; + } + } + + lmnameComposed = new Dictionary(); + + uvBuffOffsets = storage.uvBuffOffsets; + uvBuffLengths = storage.uvBuffLengths; + uvSrcBuff = storage.uvSrcBuff; + uvDestBuff = storage.uvDestBuff; + lmrIndicesOffsets = storage.lmrIndicesOffsets; + lmrIndicesLengths = storage.lmrIndicesLengths; + lmrIndicesBuff = storage.lmrIndicesBuff; + lmGroupMinLOD = storage.lmGroupMinLOD; + lmGroupLODResFlags = storage.lmGroupLODResFlags; + lmGroupLODMatrix = storage.lmGroupLODMatrix; + + userCanceled = false; + ProgressBarInit("Rendering lightmaps - preparing..."); + yield return null; + + // Init lmrebake + int lmrErrCode = lmrInit((System.IntPtr)0); + if (lmrErrCode != 0) + { + DebugLogError("Error initializing lmrebake: " + ftErrorCodes.TranslateLMRebake(lmrErrCode)); + userCanceled = true; + ProgressBarEnd(); + yield break; + } + + var groupList = new List(); + var groupListGIContributing = new List(); + if (!CollectGroups(groupList, groupListGIContributing, selectedOnly, probesOnlyL1)) yield break; + + // Prepare rendering lightmaps + var startMs = GetTimeMs(); + + var fsettings = new BinaryWriter(File.Open(scenePath + "/settings.bin", FileMode.Create)); + fsettings.Write(tileSize); + fsettings.Write(compressedGBuffer); + fsettings.Write(compressedOutput); + fsettings.Write(deviceMask); + fsettings.Close(); + + if (clientMode) ftClient.serverFileList.Add("settings.bin"); + + /*if (All.Length == 0 && AllP.Length == 0 && All2.Length == 0 && All3.Length == 0) + { + userCanceled = true; + DebugLogError("Add at least one Bakery light"); + ProgressBarEnd(); + yield break; + }*/ + + // clean up the skylight list + /* + var skylights = storage.skylights; + var skylightsDirty = storage.skylightsDirty; + var newList = new List(); + var newListB = new List(); + for(int i=0; i(); + deferredCommandsFallback = new Dictionary>(); + deferredCommandsRebake = new Dictionary(); + deferredCommandsLODGen = new Dictionary(); + deferredCommandsGIGen = new Dictionary(); + deferredCommandsHalf2VB = new Dictionary(); + deferredCommandsUVGB = new Dictionary(); + deferredFileSrc = new List(); + deferredFileDest = new List(); + deferredCommandDesc = new List(); + } + + //if (forceRefresh) // removed condition to make "Export" option work in isolation + { + lightmapMasks = new List>>(); + lightmapMaskLMNames = new List>>(); + lightmapMaskLights = new List>>(); + lightmapMaskDenoise = new List>>(); + #if UNITY_2017_3_OR_NEWER + #else + maskedLights = new List(); + #endif + lightmapHasColor = new List(); + lightmapHasMask = new List(); + lightmapHasDir = new List(); + lightmapHasRNM = new List(); + + foreach(var lmgroup in groupListGIContributingPlain) + { + var rmode = lmgroup.renderMode == (int)BakeryLightmapGroup.RenderMode.Auto ? (int)userRenderMode : (int)lmgroup.renderMode; + var dirMode = lmgroup.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)lmgroup.renderDirMode; + var dominantDirMode = dirMode == (int)ftRenderLightmap.RenderDirMode.DominantDirection; + while(lightmapMasks.Count <= lmgroup.id) + { + lightmapMasks.Add(new List>()); + lightmapMaskLMNames.Add(new List>()); + lightmapMaskLights.Add(new List>()); + lightmapMaskDenoise.Add(new List>()); + lightmapHasColor.Add(true); + lightmapHasMask.Add(rmode == (int)RenderMode.Shadowmask); + lightmapHasDir.Add(dominantDirMode); + lightmapHasRNM.Add(false); + } + } + } + + // Fix starting ray positions + if (forceRebuildGeometry) + { + if (ftBuildGraphics.exportShaderColors) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(null); + deferredCommandDesc.Add("Exporting scene - generating UV GBuffer..."); + deferredCommandsUVGB[deferredCommands.Count - 1] = true; + } + + foreach(var lmgroup in groupList) + { + var nm = lmgroup.name; + int LMID = lmgroup.id; + if (lmgroup.mode != BakeryLightmapGroup.ftLMGroupMode.Vertex || lmgroup.fixPos3D) // skip vertex colored + { + if (!adjustSamples) continue; + + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + int fixPosPasses = PASS_FLOAT; + if (giLodModeEnabled) fixPosPasses |= PASS_MASK1; + if (lmgroup.fixPos3D) + { + var mfilename = "fixPos3D_" + LMID + ".bin"; + var mf = new BinaryWriter(File.Open(scenePath + "/" + mfilename, FileMode.Create)); + mf.Write(lmgroup.voxelSize.x); + mf.Write(lmgroup.voxelSize.y); + mf.Write(lmgroup.voxelSize.z); + mf.Close(); + startInfo.Arguments = "fixpos3D " + scenePathQuoted + " \"" + "uvpos_" + nm +(compressedGBuffer ? ".lz4" : ".dds") + "\" " + fixPosPasses + " " + 0 + " " + LMID + " " + mfilename; + if (clientMode) ftClient.serverFileList.Add(mfilename); + } + else + { + startInfo.Arguments = "fixpos12 " + scenePathQuoted + " \"" + "uvpos_" + nm +(compressedGBuffer ? ".lz4" : ".dds") + "\" " + fixPosPasses + " " + 0 + " " + LMID + " " + Float2String(lmgroup.fakeShadowBias); + } + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add("Adjusting sample points for " + nm + "..."); + } + + if (giLodModeEnabled) + { + var startInfo2 = new System.Diagnostics.ProcessStartInfo(); + startInfo2.CreateNoWindow = false; + startInfo2.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo2.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo2.FileName = startInfo2.WorkingDirectory + "/" + ftraceExe; + startInfo2.CreateNoWindow = true; + startInfo2.Arguments = "lodselect " + scenePathQuoted + " \"" + "lodselect" + "\" " + PASS_FLOAT + " " + 0 + " " + LMID; + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo2); + deferredCommandDesc.Add("Calculating what is visible from " + nm + "..."); + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(null); + deferredCommandDesc.Add("Generating tracing mesh for " + lmgroup.name + "..."); + deferredCommandsLODGen[deferredCommands.Count - 1] = lmgroup.id; + } + } + } + else + { + ValidateCurrentScene(); + } + + // Render AO if needed + if (hackAOIntensity > 0 && hackAOSamples > 0) + { + foreach(var lmgroup in groupList) + { + var nm = lmgroup.name; + currentGroup = lmgroup; + bool doRender = true; + + if (doRender) { + DebugLogInfo("Preparing AO " + nm + " (" + (lmgroup.id+1) + "/" + groupList.Count + ")"); + + progressStepsDone++; + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + + if (lmgroup.probes) continue; + if (!RenderLMAO(lmgroup.id, nm)) + { + ProgressBarEnd(); + yield break; + } + } + } + } + + // Mark completely baked lights + for(int i=0; i(); + if (ulht == null) continue; + if (IsLightCompletelyBaked(obj.bakeToIndirect, userRenderMode)) + { + MarkLightAsCompletelyBaked(ulht); + } + else if (IsLightSubtractive(obj.bakeToIndirect, userRenderMode)) + { + MarkLightAsSubtractive(ulht); + } + else if (IsLightRealtime(obj.bakeToIndirect, userRenderMode)) + { + MarkLightAsRealtime(ulht); + } + } + for(int i=0; i(); + if (ulht == null) continue; + if (IsLightCompletelyBaked(obj.bakeToIndirect, userRenderMode)) + { + MarkLightAsCompletelyBaked(ulht); + } + else if (IsLightSubtractive(obj.bakeToIndirect, userRenderMode)) + { + MarkLightAsSubtractive(ulht); + } + else if (IsLightRealtime(obj.bakeToIndirect, userRenderMode)) + { + MarkLightAsRealtime(ulht); + } + } + + // Find intersecting light groups for shadowmask + bool someLightsCantBeMasked = false; + shadowmaskGroupIDToChannel = new Dictionary(); + if (userRenderMode == RenderMode.Shadowmask) + { + overlappingLights = new List(); + + //int channel = 0; + var channelBitsPerLayer = new int[32]; + + for(int i=0; i(); + if (ulht == null) continue; + if (SetupLightShadowmaskUsingBitmask(ulht, obj.bitmask, 0, channelBitsPerLayer) < 0) someLightsCantBeMasked = true; + } + + var lightsRemaining = new List(); + var lightsRemainingB = new List(); + var lightChannels = new List(); + var lightArrayIndices = new List(); + var lightIntersections = new List(); + for(int i=0; i(); + if (ulht == null) continue; + lightsRemaining.Add(ulht); + lightsRemainingB.Add(obj); + lightChannels.Add(-1); + lightArrayIndices.Add(lightArrayIndices.Count); + lightIntersections.Add(0); + } + + // Sort by the intersection count + for(int i=0; i(); // sign is type, offset is to relevant array (+1) + // Spherical + var channelBoundsPos = new List(); + var channelBoundsRadius = new List(); + // Convex + var channelBoundsConvex = new List(); + + if (la.type == LightType.Spot) + { + // Add spot geometry as pyramid + channelBoundsTypeAndOffset.Add(-(channelBoundsConvex.Count+1)); + channelBoundsConvex.Add(GetSpotConvex(la)); + } + else + { + // Add point geometry as sphere + channelBoundsTypeAndOffset.Add(channelBoundsPos.Count+1); + channelBoundsPos.Add(laPos); + channelBoundsRadius.Add(laRange); + } + + //channelBoundsPos.Add(laPos); + //channelBoundsRadius.Add(laRange); + + int channelSet = SetupLightShadowmaskUsingBitmask(la, laBitmask, lightsRemainingB[idA].shadowmaskGroupID, channelBitsPerLayer); + if (channelSet < 0) someLightsCantBeMasked = true; + + lightChannels[idA] = channelSet; + DebugLogInfo("* Light " + la.name + " set to channel " + channelSet); + //SetupLightShadowmask(la, channel); + + // Find all non-overlapping + //for(int j=i+1; j 0) + { + if (ch == 2) + { + Selection.objects = overlappingLights.ToArray(); + } + yield break; + } + } + else + { + Debug.LogError("Some shadow masks can't be baked due to more than 4 masked lights overlapping"); + } + } + + // Render directional lighting for every lightmap + ftBuildLights.InitMaps(false); + foreach(var lmgroup in groupList) + { + var nm = lmgroup.name; + currentGroup = lmgroup; + bool doRender = true; + + if (doRender) { + DebugLogInfo("Preparing (direct) lightmap " + nm + " (" + (lmgroup.id+1) + "/" + groupList.Count + ")"); + + progressStepsDone++; + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + + var routine = RenderLMDirect(lmgroup.id, nm, lmgroup.resolution); + while(routine.MoveNext()) + { + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + } + } + } + + // Save rendered light properties + for(int i=0; i 128 && lmgroup2.mode != BakeryLightmapGroup.ftLMGroupMode.Vertex) + { + if (deferredMode) + { + // Downsample via lmrebake + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(null); + if (lmgroup2.containsTerrains) + { + deferredCommandDesc.Add("Generating LOD lightmap of " + lmgroup2.name + " (terrain)..."); + } + else + { + deferredCommandDesc.Add("Generating LOD lightmap of " + lmgroup2.name + "..."); + } + deferredCommandsRebake[deferredCommands.Count - 1] = lmgroup2.GetPlainStruct(); + } + else + { + Debug.LogError("Unsupported"); + /*errCode = lmrRender(lmgroup2.name + "_diffuse_HDR" + (compressedOutput ? ".lz4" : ".dds"), + lmgroup2.name + "_diffuse_HDR_LOD", + scenePath + "/lodmask_uvpos_" + lmgroup2.name + (compressedGBuffer ? ".lz4" : ".dds"), + uvSrcBuff, uvDestBuff, uvBuffOffsets[lmgroup2.id], uvBuffLengths[lmgroup2.id], + lmrIndicesBuff, lmrIndicesOffsets[lmgroup2.id], lmrIndicesLengths[lmgroup2.id], + lmgroup2.resolution/2, lmgroup2.resolution/2, lmGroupLODResFlags[lmgroup2.id]); + if (errCode != 0) + { + DebugLogError("Error rebaking lightmap " + lmgroup2.name + ": " + ftErrorCodes.TranslateLMRebake(errCode)); + }*/ + } + } + } + } + + foreach(var lmgroup in groupList) + { + var nm = lmgroup.name; + currentGroup = lmgroup; + bool doRender = true; + + if (doRender) { + DebugLogInfo("Preparing (bounce " + i + ") lightmap " + nm + " (" + (lmgroup.id+1) + "/" + groupList.Count + ")"); + + progressStepsDone++; + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + + var rmode = lmgroup.renderMode == BakeryLightmapGroup.RenderMode.Auto ? (int)userRenderMode : (int)lmgroup.renderMode; + + if (rmode == (int)RenderMode.AmbientOcclusionOnly) continue; + + bool lastPass = i == bounces - 1; + bool needsGIPass = (lastPass && (rmode == (int)RenderMode.Indirect || rmode == (int)RenderMode.Shadowmask)); + + var dirMode = lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)lmgroup.renderDirMode; + var dominantDirMode = dirMode == (int)ftRenderLightmap.RenderDirMode.DominantDirection && lightmapHasDir[lmgroup.id]; + + if (lmgroup.probes && !lastPass) continue; // probes only need final GI pass + + if (performRendering) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(null); + deferredCommandDesc.Add("Generating GI parameters for " + lmgroup.name + "..."); + deferredCommandsGIGen[deferredCommands.Count - 1] = new Vector3(lmgroup.id, i, dominantDirMode?1:0); + + if (!RenderLMGI(lmgroup.id, nm, i, needsGIPass, lastPass)) + { + ProgressBarEnd(); + yield break; + } + + // Optionally compute SSS after bounce + if (!lmgroup.computeSSS) continue; + RenderLMSSS(lmgroup, i == bounces - 1); + } + } + } + } + + // Add directional contribution from selected lights to indirect + //if ((userRenderMode == RenderMode.Indirect || userRenderMode == RenderMode.Shadowmask) && performRendering) + { + //Debug.Log("Compositing bakeToIndirect lights with GI..."); + foreach(var lmgroup in groupListPlain) + { + string nm = lmgroup.name; + try + { + nm = lmgroup.name; + } + catch + { + DebugLogError("Error postprocessing lightmaps - see console for details"); + ProgressBarEnd(); + throw; + } + + var rmode = lmgroup.renderMode == (int)BakeryLightmapGroup.RenderMode.Auto ? (int)userRenderMode : (int)lmgroup.renderMode; + if ((rmode == (int)RenderMode.Indirect || rmode == (int)RenderMode.Shadowmask) && performRendering) + { + //int errCode2 = 0; + if (exeMode) + { + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "add " + scenePathQuoted + " \"" + nm + "_final_HDR" + (compressedOutput ? ".lz4" : ".dds") + + "\"" + " " + PASS_HALF + " " + 0 + " " + lmgroup.id + " comp_indirect" + lmgroup.id + ".bin"; + + if (deferredMode) + { + deferredFileSrc.Add("");//scenePath + "/comp_indirect" + lmgroup.id + ".bin"); + deferredFileDest.Add("");//scenePath + "/comp.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add("Compositing baked lights with GI for " + lmgroup.name + "..."); + } + else + { + /*File.Copy(scenePath + "/comp_indirect" + lmgroup.id + ".bin", scenePath + "/comp.bin", true); + Debug.Log("Running ftrace " + startInfo.Arguments); + var exeProcess = System.Diagnostics.Process.Start(startInfo); + exeProcess.WaitForExit(); + errCode2 = exeProcess.ExitCode;*/ + } + } + + var dirMode = lmgroup.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)lmgroup.renderDirMode; + var dominantDirMode = dirMode == (int)ftRenderLightmap.RenderDirMode.DominantDirection && lightmapHasDir[lmgroup.id]; + + if (dominantDirMode) + { + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "diradd " + scenePathQuoted + " \"" + nm + "_final_Dir" + (compressedOutput ? ".lz4" : ".dds") + + "\"" + " " + PASS_DIRECTION + " " + 0 + " " + lmgroup.id + " dircomp_indirect" + lmgroup.id + ".bin"; + + if (deferredMode) + { + deferredFileSrc.Add("");//scenePath + "/dircomp_indirect" + lmgroup.id + ".bin"); + deferredFileDest.Add("");//scenePath + "/dircomp.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add("Compositing baked direction for " + lmgroup.name + "..."); + } + } + } + } + } + + /* + if (bounces > 0) + { + // Remove lighting from emissive surfaces + foreach(var lmgroup in groupListPlain) + { + string nm; + try + { + nm = lmgroup.name; + } + catch + { + DebugLogError("Error postprocessing lightmaps - see console for details"); + ProgressBarEnd(); + throw; + } + bool doRender = true; + + if (lmgroup.id < 0) continue; + if (storage.hasEmissive.Count <= lmgroup.id) continue; + if (!storage.hasEmissive[lmgroup.id]) continue; + + if (doRender) { + Debug.Log("Removing emissive from " + nm + " (" + (lmgroup.id+1) + "/" + groupList.Count + ")"); + + if (performRendering) { + if (!RemoveEmissive(nm)) + { + ProgressBarEnd(); + yield break; + } + } + } + } + } + */ + + PrepareAssetImporting(); + + // Finalize lightmaps + foreach(var lmgroup in groupListPlain) + { + if (lmgroup.vertexBake && lmgroup.isImplicit && !lmgroup.probes) continue; // skip objects with scaleImLm == 0 + string nm; + try + { + nm = lmgroup.name; + } + catch + { + DebugLogError("Error postprocessing lightmaps - see console for details"); + ProgressBarEnd(); + throw; + } + bool doRender = true; + + if (doRender) { + //if (lmgroup.vertexBake) continue; // do it after the scene is loaded back + DebugLogInfo("Preparing (finalize) lightmap " + nm + " (" + (lmgroup.id+1) + "/" + groupList.Count + ")"); + + var routine = RenderLMFinalize(lmgroup.id, nm, lmgroup.resolution, lmgroup.vertexBake, lmgroup.renderDirMode, lmgroup.renderMode, lmgroup); + while(routine.MoveNext()) + { + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + } + + if (lmgroup.probes && lmgroup.name == "volumes" && lastFoundBakeableVolumes != null && lastFoundBakeableVolumes.Length > 0) + { + var vols = lastFoundBakeableVolumes; + int voffset = 0; + + var denoiseMod = GetDenoiseMode(); + var ext = (compressedOutput ? ".lz4" : ".dds"); + for(int v=0; v 0 && lmgroup.parentName != "|") + { + doRender = false; + } + + if (doRender) { + DebugLogInfo("Preparing (encode) lightmap " + nm + " (" + (lmgroup.id+1) + "/" + groupList.Count + ")"); + + var routine = RenderLMEncode(lmgroup.id, nm, lmgroup.resolution, lmgroup.vertexBake, lmgroup.renderDirMode, lmgroup.renderMode); + while(routine.MoveNext()) + { + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + } + } + } + + ftBuildGraphics.FreeTemporaryAreaLightMeshes(); + +#if UNITY_2017_3_OR_NEWER +#else + if ((userRenderMode == RenderMode.Shadowmask || userRenderMode == RenderMode.Subtractive) && (lightProbeMode != LightProbeMode.L1) || !hasAnyProbes) + { + // Generate lighting data asset + var assetName = GenerateLightingDataAssetName(); + var newPath = "Assets/" + outputPath + "/" + assetName + ".asset"; + + // Try writing the file. If it's locked, write a copy + bool locked = false; + BinaryWriter ftest = null; + try + { + ftest = new BinaryWriter(File.Open(newPath, FileMode.Create)); + } + catch + { + var index = assetName.IndexOf("_copy"); + if (index >= 0) + { + assetName = assetName.Substring(0, index); + } + else + { + assetName += "_copy"; + } + newPath = "Assets/" + outputPath + "/" + assetName + ".asset"; + locked = true; + } + if (!locked) ftest.Close(); + + if (!ftLightingDataGen.GenerateShadowmaskLightingData(newPath, ref maskedLights, userRenderMode == RenderMode.Subtractive)) + { + DebugLogError("Failed to generate LightingDataAsset"); + userCanceled = true; + yield break; + } + AssetDatabase.Refresh(); + ApplyLightingDataAsset(newPath); + EditorSceneManager.MarkAllScenesDirty(); + EditorSceneManager.SaveOpenScenes(); + } +#endif + + // Store lightmap flags + for(int s=0; s 0) + { + var vbtraceTexPosNormalArray = ftBuildGraphics.vbtraceTexPosNormalArray; + var vbtraceTexUVArray = ftBuildGraphics.vbtraceTexUVArray; + var vbtraceTexUVArrayLOD = ftBuildGraphics.vbtraceTexUVArrayLOD; + + tasks++; + flist.Write(ftClient.SERVERTASK_LODGENINIT); + flist.Write(lmGroupMinLOD.Length); + for(int j=0; j 0) + { + tasks++; + flist.Write(ftClient.SERVERTASK_COPY); + WriteString(flist, deferredFileSrc[i].Replace(scenePath, "%SCENEPATH%")); + WriteString(flist, deferredFileDest[i].Replace(scenePath, "%SCENEPATH%")); + } + + var startInfo = deferredCommands[i]; + if (startInfo != null) + { + var app = Path.GetFileNameWithoutExtension(deferredCommands[i].FileName); + if (!ftClient.app2serverTask.TryGetValue(app, out task)) + { + DebugLogError("Server doesn't support the task: " + app); + userCanceled = true; + yield break; + } + tasks++; + flist.Write(task); + WriteString(flist, startInfo.Arguments.Replace(scenePath, "%SCENEPATH%"). + Replace(Application.dataPath + "/" + outputPathFull, "%SCENEPATH%")); + } + + if (deferredCommandsUVGB.ContainsKey(i)) + { + GL.IssuePluginEvent(7); // render UVGBuffer + int uerr = 0; + while(uerr == 0) + { + uerr = ftBuildGraphics.GetUVGBErrorCode(); + yield return null; + } + + if (uerr != 0 && uerr != 99999) + { + DebugLogError("ftRenderUVGBuffer error: " + uerr); + userCanceled = true; + yield break; + } + + ftBuildGraphics.FreeAlbedoCopies(); + } + + if (deferredCommandsRebake.ContainsKey(i)) + { + var lmgroup2 = deferredCommandsRebake[i]; + if (lmgroup2.containsTerrains) + { + tasks++; + flist.Write(ftClient.SERVERTASK_LMREBAKESIMPLE); + WriteString(flist, lmgroup2.name + "_diffuse_HDR" + (compressedOutput ? ".lz4" : ".dds")); + WriteString(flist, lmgroup2.name + "_diffuse_HDR_LOD"); + flist.Write(lmgroup2.resolution/2); + flist.Write(lmgroup2.resolution/2); + flist.Write(lmgroup2.id); + } + else + { + if (lmrIndicesLengths[lmgroup2.id] == 0) + { + Debug.LogError("lmrIndicesLengths == 0 for " + lmgroup2.name); + } + else + { + tasks++; + flist.Write(ftClient.SERVERTASK_LMREBAKE); + WriteString(flist, lmgroup2.name + "_diffuse_HDR" + (compressedOutput ? ".lz4" : ".dds")); + WriteString(flist, lmgroup2.name + "_diffuse_HDR_LOD"); + WriteString(flist, "lodmask_uvpos_" + lmgroup2.name + (compressedGBuffer ? ".lz4" : ".dds")); + flist.Write(uvSrcBuff.Length); + for(int j=0; j 0) File.Copy(deferredFileSrc[i], deferredFileDest[i], true); + + var startInfo = deferredCommands[i]; + + if (startInfo != null) + { + var app = Path.GetFileNameWithoutExtension(deferredCommands[i].FileName); + DebugLogInfo("Running " + app + " " + startInfo.Arguments); + ProgressBarShow(deferredCommandDesc[i], i / (float)deferredCommands.Count, true); + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + + int errCode2 = -1; + int fallbackCtr = 0; + while(errCode2 != 0) + { +#if LAUNCH_VIA_DLL + var crt = ProcessCoroutine(app, startInfo.Arguments); + while(crt.MoveNext()) yield return null; + if (userCanceled) yield break; + errCode2 = lastReturnValue; +#else + var exeProcess = System.Diagnostics.Process.Start(startInfo); + + //exeProcess.WaitForExit(); + while(!exeProcess.HasExited) + { + yield return null; + userCanceled = simpleProgressBarCancelled(); + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + } + + errCode2 = exeProcess.ExitCode; +#endif + + if (errCode2 != 0) + { + DebugLogInfo("Error: " + ftErrorCodes.Translate(app, errCode2)); + if (deferredCommandsFallback.ContainsKey(i)) + { + DebugLogInfo("Trying fallback " +fallbackCtr); + var fallbackList = deferredCommandsFallback[i]; + if (fallbackCtr >= fallbackList.Count) break; + startInfo.Arguments = fallbackList[fallbackCtr]; + fallbackCtr++; + } + else + { + break; + } + } + } + + if (errCode2 != 0) + { + DebugLogError(app + " error: " + ftErrorCodes.Translate(app, errCode2)); + userCanceled = true; + yield break; + } + } + + if (deferredCommandsRebake.ContainsKey(i)) + { + var lmgroup2 = deferredCommandsRebake[i]; + + ProgressBarShow(deferredCommandDesc[i], i / (float)deferredCommands.Count, true); + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + + if (lmgroup2.containsTerrains) + { + DebugLogInfo("Running lmrebake (terrain) for " + lmgroup2.name + " (" + lmGroupLODResFlags[lmgroup2.id] + ")"); + int errCode2 = lmrRenderSimple(scenePath + "/" + lmgroup2.name + "_diffuse_HDR" + (compressedOutput ? ".lz4" : ".dds"), + scenePath + "/" + lmgroup2.name + "_diffuse_HDR_LOD", + lmgroup2.resolution/2, lmgroup2.resolution/2, lmGroupLODResFlags[lmgroup2.id]); + if (errCode2 != 0) + { + DebugLogError("Error rebaking lightmap (terrain) " + lmgroup2.name + ": " + ftErrorCodes.TranslateLMRebake(errCode2)); + userCanceled = true; + yield break; + } + } + else + { + if (lmrIndicesLengths[lmgroup2.id] == 0) + { + Debug.LogError("lmrIndicesLengths == 0 for " + lmgroup2.name); + } + else + { + int errCode2 = lmrRender(scenePath + "/" + lmgroup2.name + "_diffuse_HDR" + (compressedOutput ? ".lz4" : ".dds"), + scenePath + "/" + lmgroup2.name + "_diffuse_HDR_LOD", + scenePath + "/lodmask_uvpos_" + lmgroup2.name + (compressedGBuffer ? ".lz4" : ".dds"), + uvSrcBuff, uvDestBuff, uvBuffOffsets[lmgroup2.id], uvBuffLengths[lmgroup2.id], + lmrIndicesBuff, lmrIndicesOffsets[lmgroup2.id], lmrIndicesLengths[lmgroup2.id], + lmgroup2.resolution/2, lmgroup2.resolution/2, lmGroupLODResFlags[lmgroup2.id]); + if (errCode2 != 0) + { + DebugLogError("Error rebaking lightmap " + lmgroup2.name + ": " + ftErrorCodes.TranslateLMRebake(errCode2)); + userCanceled = true; + yield break; + } + } + } + } + + if (deferredCommandsLODGen.ContainsKey(i)) + { + int id = deferredCommandsLODGen[i]; + DebugLogInfo("Generating LOD vbTraceTex for " + id); + + ProgressBarShow(deferredCommandDesc[i], i / (float)deferredCommands.Count, true); + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + + int errCode2 = GenerateVBTraceTexLOD(id); + if (errCode2 != 0) + { + DebugLogError("Error generating tracing mesh for ID " + id); + userCanceled = true; + yield break; + } + } + + if (deferredCommandsGIGen.ContainsKey(i)) + { + Vector3 paramz = deferredCommandsGIGen[i]; + int id = (int)paramz.x; + int bounce = (int)paramz.y; + bool useDir = paramz.z > 0; + DebugLogInfo("Generating GI parameters for " + id+" "+bounce); + + ProgressBarShow(deferredCommandDesc[i], i / (float)deferredCommands.Count, true); + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + + string nm = ""; + int sceneLodLevel = -1; + for(int j=0; j(); + //var sanityTimeout = GetTime() + 5; + while( (sceneCount > EditorSceneManager.sceneCount || EditorSceneManager.GetSceneAt(0).path.Length == 0))// && GetTime() < sanityTimeout ) + { + yield return null; + } + for(int i=0; i(); + storages[scene] = storage; + + if (giLodModeEnabled) + { + storage.lmGroupLODResFlags = lmGroupLODResFlags; + storage.lmGroupLODMatrix = lmGroupLODMatrix; + EditorUtility.SetDirty(storage); + } + + if (loadedScenesActive[i]) EditorSceneManager.SetActiveScene(scene); + } + } + progressStepsDone = 0; + progressSteps = groupList.Count * 3; + ProgressBarSetStep(0); + } + + if (clientMode) + { + ProgressBarEnd(); + } + else + { + LoadRenderSettings(); + + var apply = ApplyBakedData(); + while(apply.MoveNext()) yield return null; + + var ms = GetTimeMs(); + double bakeTime = (ms - startMs) / 1000.0; + DebugLogInfo("Rendering finished in " + bakeTime + " seconds"); + + lastBakeTime = (int)bakeTime; + if (renderSettingsStorage == null) renderSettingsStorage = FindRenderSettingsStorage(); + if (renderSettingsStorage != null) renderSettingsStorage.lastBakeTime = lastBakeTime; + + try + { + var bakeTimeLog = new StreamWriter(File.Open("bakery_times.log", FileMode.Append)); + if (bakeTimeLog != null) + { + int hours = lastBakeTime / (60*60); + int minutes = (lastBakeTime / 60) % 60; + int seconds = lastBakeTime % 60; + bakeTimeLog.Write(System.DateTime.Now.ToString("MM/dd/yyyy HH:mm") + " | " + EditorSceneManager.GetActiveScene().name + " | " + hours+"h "+minutes+"m "+seconds+"s\n"); + } + bakeTimeLog.Close(); + } + catch + { + Debug.LogError("Failed writing bakery_times.log"); + } + + ProgressBarEnd(); + + if (beepOnFinish) System.Media.SystemSounds.Beep.Play(); + + if (OnFinishedFullRender != null) + { + OnFinishedFullRender.Invoke(this, null); + } + } + } + + + IEnumerator ApplyBakedData() + { + var sceneCount = EditorSceneManager.sceneCount; + var bdataName = "BakeryPrefabLightmapData"; + + // Load vertex colors + try + { + foreach(var lmgroup in groupListGIContributingPlain) + { + if (!lmgroup.vertexBake) continue; + if (lmgroup.isImplicit) continue; + + bool hasShadowMask = lmgroup.renderMode == (int)BakeryLightmapGroup.RenderMode.Shadowmask || + (lmgroup.renderMode == (int)BakeryLightmapGroup.RenderMode.Auto && userRenderMode == RenderMode.Shadowmask); + + bool hasDir = lmgroup.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.DominantDirection || + (lmgroup.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.Auto && renderDirMode == RenderDirMode.DominantDirection); + + bool hasSH = lmgroup.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.SH || + (lmgroup.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.Auto && renderDirMode == RenderDirMode.SH); + + GenerateVertexBakedMeshes(lmgroup.id, lmgroup.name, hasShadowMask, hasDir, hasSH); + } + } + catch + { + DebugLogError("Error loading vertex colors - see console for details"); + ProgressBarEnd(); + throw; + } + + // Set probe colors + if (!selectedOnly && lightProbeMode == LightProbeMode.L1 && hasAnyProbes && !fullSectorRender) + { + var probes = LightmapSettings.lightProbes; + if (probes == null) + { + DebugLogError("No probes in LightingDataAsset"); + yield break; + } + var positions = probes.positions; + int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)probes.count)); + atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)tileSize) * tileSize; + + var shs = new SphericalHarmonicsL2[probes.count]; + + int r = 0; + int g = 1; + int b = 2; + + var l0 = new float[atlasTexSize * atlasTexSize * 4]; + var l1x = new float[atlasTexSize * atlasTexSize * 4]; + var l1y = new float[atlasTexSize * atlasTexSize * 4]; + var l1z = new float[atlasTexSize * atlasTexSize * 4]; + var handle = GCHandle.Alloc(l0, GCHandleType.Pinned); + var handleL1x = GCHandle.Alloc(l1x, GCHandleType.Pinned); + var handleL1y = GCHandle.Alloc(l1y, GCHandleType.Pinned); + var handleL1z = GCHandle.Alloc(l1z, GCHandleType.Pinned); + var errCodes = new int[4]; + try + { + var pointer = handle.AddrOfPinnedObject(); + var pointerL1x = handleL1x.AddrOfPinnedObject(); + var pointerL1y = handleL1y.AddrOfPinnedObject(); + var pointerL1z = handleL1z.AddrOfPinnedObject(); + errCodes[0] = halffloat2vb(scenePath + "\\probes_final_L0" + (compressedOutput ? ".lz4" : ".dds"), pointer, 2); + errCodes[1] = halffloat2vb(scenePath + "\\probes_final_L1x" + (compressedOutput ? ".lz4" : ".dds"), pointerL1x, 2); + errCodes[2] = halffloat2vb(scenePath + "\\probes_final_L1y" + (compressedOutput ? ".lz4" : ".dds"), pointerL1y, 2); + errCodes[3] = halffloat2vb(scenePath + "\\probes_final_L1z" + (compressedOutput ? ".lz4" : ".dds"), pointerL1z, 2); + bool ok = true; + for(int i=0; i<4; i++) + { + if (errCodes[i] != 0) + { + Debug.LogError("hf2vb (" + i + "): " + errCodes[i]); + ok = false; + } + } + if (ok) + { + for(int i=0; i(); + while( (sceneCount > EditorSceneManager.sceneCount || EditorSceneManager.GetSceneAt(0).path.Length == 0))// && GetTime() < sanityTimeout ) + { + yield return null; + } + for(int i=0; i(); + storages[scene] = storage; + } + } +#endif + + probes.bakedProbes = shs; + EditorUtility.SetDirty(Lightmapping.lightingDataAsset); + } + + LoadVolumes(); + + //EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); + EditorSceneManager.MarkAllScenesDirty(); + + // Asset importing stage 1: set AssetPostprocessor settings -> moved + + // Asset importing stage 2: actual import + AssetDatabase.Refresh(); + ftTextureProcessor.texSettings = new Dictionary(); + + // Asset importing stage 3: load and assign imported assets + foreach(var lmgroup in groupListGIContributingPlain) + { + if (lmgroup.vertexBake) continue; + if (lmgroup.parentName != null && lmgroup.parentName.Length > 0 && lmgroup.parentName != "|") continue; + var nm = lmgroup.name; + + var dirMode = lmgroup.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)lmgroup.renderDirMode; + var dominantDirMode = dirMode == (int)ftRenderLightmap.RenderDirMode.DominantDirection && lightmapHasDir[lmgroup.id]; + var rnmMode = dirMode == (int)ftRenderLightmap.RenderDirMode.RNM && lightmapHasRNM[lmgroup.id]; + var shMode = dirMode == (int)ftRenderLightmap.RenderDirMode.SH && lightmapHasRNM[lmgroup.id]; + var shModeProbe = dirMode == (int)BakeryLightmapGroup.RenderDirMode.ProbeSH && lightmapHasRNM[lmgroup.id]; + if (shModeProbe) shMode = true; + + Texture2D lm = null; + var outfile = "Assets/" + outputPathFull + "/"+nm+"_final.hdr"; + if (rnmMode) outfile = "Assets/" + outputPathFull + "/"+nm+"_RNM0.hdr"; + if (lightmapHasColor[lmgroup.id] && File.Exists(outfile)) + { + lm = AssetDatabase.LoadAssetAtPath(outfile, typeof(Texture2D)) as Texture2D; + } + + Texture2D mask = null; + if (lightmapHasMask[lmgroup.id]) + { + outfile = "Assets/" + outputPathFull + "/"+nm+"_mask" + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga"); + if (File.Exists(outfile)) + { + mask = AssetDatabase.LoadAssetAtPath(outfile, typeof(Texture2D)) as Texture2D; + } + } + + Texture2D dirLightmap = null; + if (dominantDirMode) + { + outfile = "Assets/" + outputPathFull + "/"+nm+"_dir" + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga"); + if (File.Exists(outfile)) + { + dirLightmap = AssetDatabase.LoadAssetAtPath(outfile, typeof(Texture2D)) as Texture2D; + } + } + + Texture2D rnmLightmap0 = null; + Texture2D rnmLightmap1 = null; + Texture2D rnmLightmap2 = null; + if (rnmMode) + { + for(int c=0; c<3; c++) + { + outfile = "Assets/" + outputPathFull + "/"+nm+"_RNM" + c + ".hdr"; + if (c == 0) rnmLightmap0 = AssetDatabase.LoadAssetAtPath(outfile, typeof(Texture2D)) as Texture2D; + if (c == 1) rnmLightmap1 = AssetDatabase.LoadAssetAtPath(outfile, typeof(Texture2D)) as Texture2D; + if (c == 2) rnmLightmap2 = AssetDatabase.LoadAssetAtPath(outfile, typeof(Texture2D)) as Texture2D; + } + } + + if (shMode) + { + outfile = "Assets/" + outputPathFull + "/"+nm+"_L0.hdr"; + lm = AssetDatabase.LoadAssetAtPath(outfile, typeof(Texture2D)) as Texture2D; + for(int c=0; c<3; c++) + { + string comp; + if (c==0) + { + comp = "x"; + } + else if (c==1) + { + comp = "y"; + } + else + { + comp = "z"; + } + outfile = "Assets/" + outputPathFull + "/"+nm+"_L1" + comp + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga"); + if (c == 0) rnmLightmap0 = AssetDatabase.LoadAssetAtPath(outfile, typeof(Texture2D)) as Texture2D; + if (c == 1) rnmLightmap1 = AssetDatabase.LoadAssetAtPath(outfile, typeof(Texture2D)) as Texture2D; + if (c == 2) rnmLightmap2 = AssetDatabase.LoadAssetAtPath(outfile, typeof(Texture2D)) as Texture2D; + } + } + + for(int s=0; s 0) + { + for(int s=0; s(); + ftLightmapsStorage.SectorData sect = null; + for(int sc=0; sc 1 && removeDuplicateLightmaps) + { + for(int s=0; s(); + var origID2New = new Dictionary(); + for(int i=0; i storage.maps.Count) continue; + usedIDs[storage.bakedIDs[i]] = true; + } + for(int i=0; i storage.maps.Count) continue; + usedIDs[storage.bakedIDsTerrain[i]] = true; + } + var newMaps = new List(); + var newMasks = new List(); + var newDirMaps = new List(); + var newRNM0Maps = new List(); + var newRNM1Maps = new List(); + var newRNM2Maps = new List(); + var newMapsMode = new List(); + foreach(var pair in usedIDs) + { + int origID = pair.Key; + int newID = newMaps.Count; + origID2New[origID] = newID; + + newMaps.Add(storage.maps[origID]); + if (storage.masks.Count > origID) newMasks.Add(storage.masks[origID]); + if (storage.dirMaps.Count > origID) newDirMaps.Add(storage.dirMaps[origID]); + if (storage.rnmMaps0.Count > origID) + { + newRNM0Maps.Add(storage.rnmMaps0[origID]); + newRNM1Maps.Add(storage.rnmMaps1[origID]); + newRNM2Maps.Add(storage.rnmMaps2[origID]); + newMapsMode.Add(storage.mapsMode[origID]); + } + } + storage.maps = newMaps; + storage.masks = newMasks; + storage.dirMaps = newDirMaps; + storage.rnmMaps0 = newRNM0Maps; + storage.rnmMaps1 = newRNM1Maps; + storage.rnmMaps2 = newRNM2Maps; + storage.mapsMode = newMapsMode; + + for(int i=0; i storage.maps.Count) continue; + storage.bakedIDs[i] = newID; + } + + for(int i=0; i storage.maps.Count) continue; + storage.bakedIDsTerrain[i] = newID; + } + } + } + + // Patch lightmapped prefabs + //var bdataName = "BakeryPrefabLightmapData"; + var lmprefabs = FindObjectsOfType(typeof(BakeryLightmappedPrefab)) as BakeryLightmappedPrefab[]; + for(int i=0; i(); + if (pstore == null) pstore = pstoreT.gameObject.AddComponent(); + + var prenderers = p.GetComponentsInChildren(); + var pterrains = p.GetComponentsInChildren(); + var plights = p.GetComponentsInChildren(); + + var storage = storages[p.gameObject.scene]; + + pstore.bakedRenderers = new List(); + pstore.bakedIDs = new List(); + pstore.bakedScaleOffset = new List(); + pstore.bakedVertexColorMesh = new List(); + + pstore.bakedRenderersTerrain = new List(); + pstore.bakedIDsTerrain = new List(); + pstore.bakedScaleOffsetTerrain = new List(); + + pstore.bakedLights = new List(); + pstore.bakedLightChannels = new List(); + var usedIDs = new Dictionary(); + usedIDs[0] = true; // have to include ID 0 because Unity judges lightmap compression by it + + for(int j=0; j(); + pstore.masks = new List(); + pstore.dirMaps = new List(); + pstore.rnmMaps0 = new List(); + pstore.rnmMaps1 = new List(); + pstore.rnmMaps2 = new List(); + pstore.mapsMode = new List(); + foreach(var pair in usedIDs) + { + int id = pair.Key; + if (id < 0) continue; + while(pstore.maps.Count <= id) + { + pstore.maps.Add(null); + if (storage.masks.Count > pstore.masks.Count) pstore.masks.Add(null); + if (storage.dirMaps.Count > pstore.dirMaps.Count) pstore.dirMaps.Add(null); + if (storage.rnmMaps0.Count > pstore.rnmMaps0.Count) + { + pstore.rnmMaps0.Add(null); + pstore.rnmMaps1.Add(null); + pstore.rnmMaps2.Add(null); + pstore.mapsMode.Add(0); + } + } + if (storage.maps.Count > id) + { + pstore.maps[id] = storage.maps[id]; + if (pstore.masks.Count > id) pstore.masks[id] = storage.masks[id]; + if (pstore.dirMaps.Count > id) pstore.dirMaps[id] = storage.dirMaps[id]; + if (pstore.rnmMaps0.Count > id) + { + pstore.rnmMaps0[id] = storage.rnmMaps0[id]; + pstore.rnmMaps1[id] = storage.rnmMaps1[id]; + pstore.rnmMaps2[id] = storage.rnmMaps2[id]; + pstore.mapsMode[id] = storage.mapsMode[id]; + } + } + } + +#if UNITY_2018_3_OR_NEWER + // Unity 2018.3 incorrectly sets lightmap IDs when applying prefabs, UNLESS editor is focused + DebugLogInfo("Waiting for Unity editor focus..."); + bool focused = false; + while(!focused) + { + var wnd = GetForegroundWindow(); + while(wnd != (System.IntPtr)0) + { + if (wnd == unityEditorHWND) + { + focused = true; + break; + } + wnd = GetParent(wnd); + } + yield return null; + } +#endif + + PrefabUtility.ReplacePrefab(p.gameObject, PrefabUtility.GetPrefabParent(p.gameObject), ReplacePrefabOptions.ConnectToPrefab); + DebugLogInfo("Patched prefab " + p.name); + } + + ftLightmaps.RefreshFull(); + } + + static void MergeSectorsUpdate() + { + if (loadedScenes != null) + { + if ( (loadedScenes.Count > EditorSceneManager.sceneCount || EditorSceneManager.GetSceneAt(0).path.Length == 0)) + { + DebugLogInfo("MergeSectors: waiting for scenes..."); + return; + } + MergeSectors(); + DebugLogInfo("MergeSectors: done"); + } + EditorApplication.update -= MergeSectorsUpdate; + } + + static void MergeSectorsDeferred() + { + EditorApplication.update += MergeSectorsUpdate; + } + + static public void MergeSectors() + { + int sceneCount = EditorSceneManager.sceneCount; + CollectStorages(); + + for(int s=0; s(); + var newMasks = new List(); + var newDirMaps = new List(); + var newRNM0Maps = new List(); + var newRNM1Maps = new List(); + var newRNM2Maps = new List(); + var newMapsMode = new List(); + var newBakedRenderers = new List(); + var newBakedRenderersTerrain = new List(); + var newBakedIDs = new List(); + var newBakedIDsTerrain = new List(); + var newBakedScaleOffset = new List(); + var newBakedScaleOffsetTerrain = new List(); + var newBakedVertexColorMesh = new List(); + HashSet nonBakedSet = null; + bool anyMasks = false; + bool anyDirMaps = false; + bool anyRNMMaps = false; + int maxMapCount = 0; + for(int sc=0; sc 0) anyMasks = true; + if (sect.dirMaps != null && sect.dirMaps.Count > 0) anyDirMaps = true; + if (sect.rnmMaps0 != null && sect.rnmMaps0.Count > 0) anyRNMMaps = true; + + if (sect.maps != null) maxMapCount = System.Math.Max(maxMapCount, sect.maps.Count); + } + + var idRemap = new int[maxMapCount]; + + var rendererSet = new HashSet(); + var terrainSet = new HashSet(); + + for(int sc=storage.sectors.Count-1; sc>=0; sc--) // revert order because newest sectors have priority over the global sector + //for(int sc=0; sc 0); + bool hasDirMaps = (sect.dirMaps != null && sect.dirMaps.Count > 0); + bool hasRNMMaps = (sect.rnmMaps0 != null && sect.rnmMaps0.Count > 0); + for(int j=0; j= 0) + { + idRemap[j] = exists; + continue; + } + + idRemap[j] = newMaps.Count; + newMaps.Add(sect.maps[j]); + + bool has = hasMasks && sect.masks.Count > j; + if (anyMasks) newMasks.Add(has ? sect.masks[j] : null); + + has = hasDirMaps && sect.dirMaps.Count > j; + if (anyDirMaps) + { + newDirMaps.Add(has ? sect.dirMaps[j] : null); + } + + if (anyRNMMaps) + { + has = hasRNMMaps && sect.rnmMaps0.Count > j; + newRNM0Maps.Add(has ? sect.rnmMaps0[j] : null); + newRNM1Maps.Add(has ? sect.rnmMaps1[j] : null); + newRNM2Maps.Add(has ? sect.rnmMaps2[j] : null); + newMapsMode.Add(has ? sect.mapsMode[j] : 0); + } + } + + for(int j=0; j j ? sect.bakedVertexColorMesh[j] : null); + + int id = sect.bakedIDs[j]; + if (id >= 0 && id != 0xFFFF) + { + if (idRemap.Length > id) id = idRemap[id]; + } + newBakedIDs.Add(id); + } + for(int j=0; j= 0 && id != 0xFFFF) + { + if (idRemap.Length > id) id = idRemap[id]; + } + newBakedIDsTerrain.Add(id); + } + + if (nonBakedSet == null) + { + nonBakedSet = new HashSet(sect.nonBakedRenderers); + } + else + { + nonBakedSet.IntersectWith(sect.nonBakedRenderers); + } + } + storage.maps = newMaps; + storage.masks = newMasks; + storage.dirMaps = newDirMaps; + storage.rnmMaps0 = newRNM0Maps; + storage.rnmMaps1 = newRNM1Maps; + storage.rnmMaps2 = newRNM2Maps; + storage.mapsMode = newMapsMode; + storage.bakedRenderers = newBakedRenderers; + storage.bakedRenderersTerrain = newBakedRenderersTerrain; + storage.bakedIDs = newBakedIDs; + storage.bakedIDsTerrain = newBakedIDsTerrain; + storage.bakedScaleOffset = newBakedScaleOffset; + storage.bakedScaleOffsetTerrain = newBakedScaleOffsetTerrain; + storage.bakedVertexColorMesh = newBakedVertexColorMesh; + storage.nonBakedRenderers = new List(nonBakedSet); + + EditorUtility.SetDirty(storage); + EditorSceneManager.MarkAllScenesDirty(); + + ftLightmaps.RefreshFull(); + } + } + + void FindAllReflectionProbesAndDisable() + { + var found = FindObjectsOfType(typeof(ReflectionProbe))as ReflectionProbe[]; + for(int i = 0; i < found.Length; i++) + { + reflectionProbes.Add(found[i]); + found[i].enabled = false; + } + } + + void ReEnableReflectionProbes() + { + for(int i = 0; i < reflectionProbes.Count; i++) + { + if (reflectionProbes[i] != null) reflectionProbes[i].enabled = true; + } + } + + public static int GetID(Object obj) + { + int uid = ftUniqueIDRegistry.GetUID(obj.GetInstanceID()); + if (uid == -1) + { + var s = (obj as GameObject).GetComponent(); + if (s != null) s.Start(); + var d = (obj as GameObject).GetComponent(); + if (d != null) d.Start(); + var p = (obj as GameObject).GetComponent(); + if (p != null) p.Start(); + var l = (obj as GameObject).GetComponent(); + if (l != null) l.Start(); + + uid = ftUniqueIDRegistry.GetUID(obj.GetInstanceID()); + if (uid == -1) + { + Debug.LogError("GetUID failed for " + obj.name); + } + } + return uid; + } + + string GetLightName(GameObject obj, int lmid) + { + return "light_" + GetID(obj) + "_" + lmid; + } + + bool IsLightDirty(BakeryLightMesh light) + { + if (forceRefresh) return true; + + storage = storages[light.gameObject.scene]; + ftLightmapsStorage.LightData data; + if (!storage.lightsDict.TryGetValue(GetID(light.gameObject), out data)) { + return true; // not stored + } + + if (light.color != data.color) { + return true; + } + if (light.intensity != data.intensity) { + return true; + } + if (light.cutoff != data.range) { + return true; + } + if (light.samples != data.samples) { + return true; + } + if (light.samples2 != data.samples2) { + return true; + } + if (light.selfShadow != data.selfShadow) { + return true; + } + if (light.bakeToIndirect != data.bakeToIndirect) { + return true; + } + + var tform1 = light.GetComponent().localToWorldMatrix; + var tform2 = data.tform; + for(int y=0; y<4; y++) { + for(int x=0; x<4; x++) { + if (tform1[x,y] != tform2[x,y]) { + return true; + } + } + } + + return false; + } + + bool IsLightDirty(BakeryPointLight light) + { + if (forceRefresh) return true; + + storage = storages[light.gameObject.scene]; + ftLightmapsStorage.LightData data; + if (!storage.lightsDict.TryGetValue(GetID(light.gameObject), out data)) { + return true; // not stored + } + + if (light.color != data.color) { + return true; + } + if (light.intensity != data.intensity) { + return true; + } + if (light.cutoff != data.range) { + return true; + } + if (light.shadowSpread != data.radius) { + return true; + } + if (light.samples != data.samples) { + return true; + } + if (light.realisticFalloff != data.realisticFalloff) + { + return true; + } + if ((int)light.projMode != data.projMode) + { + return true; + } + Object cookie = null; + if (light.projMode == BakeryPointLight.ftLightProjectionMode.Cubemap) + { + cookie = light.cubemap; + } else if (light.projMode == BakeryPointLight.ftLightProjectionMode.Cookie) + { + cookie = light.cookie; + } else if (light.projMode == BakeryPointLight.ftLightProjectionMode.IES) + { + cookie = light.iesFile; + } + if (cookie != data.cookie) return true; + + if (light.angle != data.angle) return true; + + if (light.bakeToIndirect != data.bakeToIndirect) { + return true; + } + + //if (light.texName != data.texName) return true; + + var tform1 = light.GetComponent().localToWorldMatrix; + var tform2 = data.tform; + for(int y=0; y<4; y++) { + for(int x=0; x<4; x++) { + if (tform1[x,y] != tform2[x,y]) { + return true; + } + } + } + + return false; + } + + public static bool IsLightDirty(BakeryDirectLight light) + { + if (forceRefresh) return true; + + storage = storages[light.gameObject.scene]; + ftLightmapsStorage.LightData data; + if (!storage.lightsDict.TryGetValue(GetID(light.gameObject), out data)) return true; // not stored + + if (light.color != data.color) { + return true; + } + if (light.intensity != data.intensity) { + return true; + } + if (light.shadowSpread != data.radius) { + return true; + } + if (light.samples != data.samples) { + return true; + } + + if (light.bakeToIndirect != data.bakeToIndirect) { + return true; + } + + var tform1 = light.GetComponent().localToWorldMatrix; + var tform2 = data.tform; + for(int y=0; y<4; y++) { + for(int x=0; x<4; x++) { + if (tform1[x,y] != tform2[x,y]) { + return true; + } + } + } + + return false; + } + + bool IsLightDirty(BakerySkyLight light) + { + if (forceRefresh) return true; + + storage = storages[light.gameObject.scene]; + ftLightmapsStorage.LightData data; + if (!storage.lightsDict.TryGetValue(GetID(light.gameObject), out data)) return true; // not stored + + if (light.color != data.color) return true; + if (light.intensity != data.intensity) return true; + //if (light.texName != data.texName) return true; + if (light.samples != data.samples) { + return true; + } + if (light.bakeToIndirect != data.bakeToIndirect) { + return true; + } + if (light.cubemap != data.cookie) + { + return true; + } + + return false; + } + + void StoreLight(BakeryLightMesh light) + { + storage = storages[light.gameObject.scene]; + ftLightmapsStorage.LightData data; + int uid = GetID(light.gameObject); + if (!storage.lightsDict.TryGetValue(uid, out data) || data == null) + { + data = new ftLightmapsStorage.LightData(); + storage.StoreLight(uid, data); + } + data.color = light.color; + data.intensity = light.intensity; + data.range = light.cutoff; + data.samples = light.samples; + data.samples2 = light.samples2; + data.selfShadow = light.selfShadow; + data.bakeToIndirect = light.bakeToIndirect; + data.tform = light.GetComponent().localToWorldMatrix; + } + + void StoreLight(BakeryPointLight light) + { + storage = storages[light.gameObject.scene]; + ftLightmapsStorage.LightData data; + int uid = GetID(light.gameObject); + if (!storage.lightsDict.TryGetValue(uid, out data) || data == null) + { + data = new ftLightmapsStorage.LightData(); + storage.StoreLight(uid, data); + } + //var unityLight = light.GetComponent(); + data.color = light.color; + data.intensity = light.intensity; + data.radius = light.shadowSpread; + data.range = light.cutoff; + data.samples = light.samples; + data.bakeToIndirect = light.bakeToIndirect; + + data.realisticFalloff = light.realisticFalloff; + data.projMode = (int)light.projMode; + if (light.projMode == BakeryPointLight.ftLightProjectionMode.Cubemap) + { + data.cookie = light.cubemap; + } else if (light.projMode == BakeryPointLight.ftLightProjectionMode.Cookie) + { + data.cookie = light.cookie; + } else if (light.projMode == BakeryPointLight.ftLightProjectionMode.IES) + { + data.cookie = light.iesFile; + } + data.angle = light.angle; + + //data.texName = light.texName; // TODO: check for cubemap! (and sky too) + data.tform = light.GetComponent().localToWorldMatrix; + } + + void StoreLight(BakeryDirectLight light) + { + storage = storages[light.gameObject.scene]; + ftLightmapsStorage.LightData data; + int uid = GetID(light.gameObject); + if (!storage.lightsDict.TryGetValue(uid, out data) || data == null) + { + data = new ftLightmapsStorage.LightData(); + storage.StoreLight(uid, data); + } + data.color = light.color; + data.intensity = light.intensity; + data.radius = light.shadowSpread; + data.samples = light.samples; + data.bakeToIndirect = light.bakeToIndirect; + data.tform = light.GetComponent().localToWorldMatrix; + } + + void StoreLight(BakerySkyLight light) + { + storage = storages[light.gameObject.scene]; + ftLightmapsStorage.LightData data; + int uid = GetID(light.gameObject); + if (!storage.lightsDict.TryGetValue(uid, out data) || data == null) + { + data = new ftLightmapsStorage.LightData(); + storage.StoreLight(uid, data); + } + data.color = light.color; + data.intensity = light.intensity; + data.range = 0; + data.samples = light.samples; + data.bakeToIndirect = light.bakeToIndirect; + data.tform = Matrix4x4.identity; + //data.texName = light.texName; + data.cookie = light.cubemap; + } + + void UpdateLightmapShadowmaskFromPointLight(BakeryPointLight obj, int LMID, string lname, string lmname) + { + if (userRenderMode == RenderMode.Shadowmask && obj.shadowmask) + { + var ulht = obj.GetComponent(); + if (ulht == null) + { + DebugLogWarning("Light " + obj.name + " set to shadowmask, but doesn't have real-time light");; + } + else + { + UpdateMaskArray(LMID, lname, lmname, ulht, false); + } + } + } + + bool WriteCompFiles(BakeryPointLight obj, ComposeInstructionFiles cif, string lname, int rmode, bool dominantDirMode, bool rnmMode, bool shMode) + { + bool usesIndirectIntensity = false; + + cif.fcomp.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + if (bounces > 0) + { + cif.fcomp.Write(obj.indirectIntensity * hackIndirectBoost); + if (Mathf.Abs(obj.indirectIntensity - 1.0f) > 0.01f) usesIndirectIntensity = true; + } + + if ((rmode == (int)RenderMode.Indirect || rmode == (int)RenderMode.Shadowmask) + && obj.bakeToIndirect) + { + cif.fcompIndirect.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + if (cif.fcompDirIndirect != null) + { + cif.fcompDirIndirect.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompDirIndirect.Write(lname + "_Dir" + (compressedOutput ? ".lz4" : ".dds")); + } + } + + bool rmodeFullLight = (rmode == (int)RenderMode.FullLighting || rmode == (int)RenderMode.Subtractive); + + if (dominantDirMode && (rmodeFullLight || obj.bakeToIndirect)) + { + cif.fcompDir.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompDir.Write(lname + "_Dir" + (compressedOutput ? ".lz4" : ".dds")); + } + else if (rnmMode && (rmodeFullLight || obj.bakeToIndirect)) + { + cif.fcompRNM0.Write(lname + "_RNM0" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompRNM1.Write(lname + "_RNM1" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompRNM2.Write(lname + "_RNM2" + (compressedOutput ? ".lz4" : ".dds")); + } + else if (shMode && (rmodeFullLight || obj.bakeToIndirect)) + { + cif.fcompSH.Write(lname + "_RNM0" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM1" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM2" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM3" + (compressedOutput ? ".lz4" : ".dds")); + } + + return usesIndirectIntensity; + } + + void AddPointLightCommandLine(string renderMode, string lname, string settingsFile, string progressText, int LMID, BakeryPointLight obj, + int rmode, bool dominantDirMode, bool rnmMode, bool shMode, bool shModeProbe) + { + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + + bool rmodeFullLight = (rmode == (int)RenderMode.FullLighting || rmode == (int)RenderMode.Subtractive); + + int passes = PASS_HALF; + if (dominantDirMode && (rmodeFullLight || obj.bakeToIndirect)) + { + passes |= PASS_DIRECTION; + } + else if (rnmMode && (rmodeFullLight || obj.bakeToIndirect)) + { + renderMode += "rnm"; + if (bounces == 0) passes = 0; + passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2; + } + else if (shMode && (rmodeFullLight || obj.bakeToIndirect)) + { + renderMode += shModeProbe ? "probesh" : "sh"; + if (bounces == 0) passes = 0; + passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2 | PASS_RNM3; + } + if (userRenderMode == RenderMode.Shadowmask && obj.shadowmask) + { + passes |= PASS_MASK; + if (currentGroup.transparentSelfShadow) passes |= PASS_MASK1; + } + + startInfo.Arguments = renderMode + " " + scenePathQuoted + " \"" + lname + "\" " + passes + " " + 0 + " " + LMID + " " + settingsFile; + + deferredFileSrc.Add("");//scenePath + "/pointlight" + i + ".bin"); + deferredFileDest.Add("");//scenePath + "/pointlight.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + } + + string PrepareBatchPointLight(int start, int end, int LMID, bool[] skipLight, ComposeInstructionFiles cif, int rmode, bool dominantDirMode, bool rnmMode, bool shMode, ref bool usesIndirectIntensity) + { + string lname = "PointBatch_" + LMID + "_" + start + "_" + end; + bool first = true; + //Debug.LogError("----- Group:"); + for(int j=start; j<=end; j++) + { + if (skipLight[j]) continue; + + //Debug.LogError(AllP[j]); + + // For every light in a batch + UpdateLightmapShadowmaskFromPointLight(AllP[j], LMID, lname, currentGroup.name); + if (first) + { + // Once for the whole batch + if (WriteCompFiles(AllP[j], cif, lname, rmode, dominantDirMode, rnmMode, shMode)) usesIndirectIntensity = true; + first = false; + } + } + return lname; + } + + class ComposeInstructionFiles + { + public BinaryWriter fcomp = null; + public BinaryWriter fcompIndirect = null; + public BinaryWriter fcompDir = null; + public BinaryWriter fcompDirIndirect = null; + public BinaryWriter fcompRNM0 = null; + public BinaryWriter fcompRNM1 = null; + public BinaryWriter fcompRNM2 = null; + public BinaryWriter fcompSH = null; + + public void Close() + { + if (fcomp != null) fcomp.Close(); + if (fcompIndirect != null) fcompIndirect.Close(); + if (fcompDirIndirect != null) fcompDirIndirect.Close(); + if (fcompDir != null) fcompDir.Close(); + if (fcompRNM0 != null) fcompRNM0.Close(); + if (fcompRNM1 != null) fcompRNM1.Close(); + if (fcompRNM2 != null) fcompRNM2.Close(); + if (fcompSH != null) fcompSH.Close(); + } + } + + IEnumerator RenderLMDirect(int LMID, string lmname, int resolution) + { + System.Diagnostics.ProcessStartInfo startInfo; + //System.Diagnostics.Process exeProcess; + + bool doCompose = exeMode; + + var cif = new ComposeInstructionFiles(); + + long fcompStartPos = 0; + bool usesIndirectIntensity = Mathf.Abs(hackIndirectBoost - 1.0f) > 0.001f; + var rmode = currentGroup.renderMode == BakeryLightmapGroup.RenderMode.Auto ? (int)userRenderMode : (int)currentGroup.renderMode; + var dirMode = currentGroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)currentGroup.renderDirMode; + var dominantDirMode = dirMode == (int)ftRenderLightmap.RenderDirMode.DominantDirection; + var rnmMode = dirMode == (int)ftRenderLightmap.RenderDirMode.RNM; + var shMode = dirMode == (int)ftRenderLightmap.RenderDirMode.SH; + var shModeProbe = dirMode == (int)BakeryLightmapGroup.RenderDirMode.ProbeSH; + if (shModeProbe) shMode = true; + + if (rmode == (int)RenderMode.AmbientOcclusionOnly) + { + if (dominantDirMode) lightmapHasDir[LMID] = true; + yield break; + } + + bool rmodeFullLight = (rmode == (int)RenderMode.FullLighting || rmode == (int)RenderMode.Subtractive); + + lightmapHasMask[LMID] = false; + + if (doCompose) + { + var fcompName = "comp_" + LMID + ".bin"; + cif.fcomp = new BinaryWriter(File.Open(scenePath + "/" + fcompName, FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add(fcompName); + if (bounces > 0) + { + cif.fcomp.Write(false); + cif.fcomp.Write("uvalbedo_" + lmname + (compressedGBuffer ? ".lz4" : ".dds")); + + if (storage.hasEmissive.Count > LMID && storage.hasEmissive[LMID]) + { + cif.fcomp.Write("uvemissive_" + lmname + (compressedGBuffer ? ".lz4" : ".dds")); + } + else + { + cif.fcomp.Write(""); + } + } + + if (rmode == (int)RenderMode.Indirect || rmode == (int)RenderMode.Shadowmask) + { + cif.fcompIndirect = new BinaryWriter(File.Open(scenePath + "/comp_indirect" + LMID + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("comp_indirect" + LMID + ".bin"); + if (bounces > 0) + { + cif.fcompIndirect.Write(lmname + "_final_HDR2" + (compressedOutput ? ".lz4" : ".dds")); + } + if (dominantDirMode) + { + cif.fcompDirIndirect = new BinaryWriter(File.Open(scenePath + "/dircomp_indirect" + LMID + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("dircomp_indirect" + LMID + ".bin"); + cif.fcompDirIndirect.Write("uvnormal_" + lmname + (compressedGBuffer ? ".lz4" : ".dds")); + if (bounces > 0) + { + cif.fcompDirIndirect.Write(lmname + "_final_HDR2" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompDirIndirect.Write(lmname + "_final_Dir" + (compressedOutput ? ".lz4" : ".dds")); + } + } + } + if (dominantDirMode) + { + cif.fcompDir = new BinaryWriter(File.Open(scenePath + (deferredMode ? "/dircomp_" + LMID + ".bin" : "/dircomp.bin"), FileMode.Create)); + cif.fcompDir.Write("uvnormal_" + lmname + (compressedGBuffer ? ".lz4" : ".dds")); + if (clientMode) ftClient.serverFileList.Add("dircomp_" + LMID + ".bin"); + } + if (rnmMode) + { + cif.fcompRNM0 = new BinaryWriter(File.Open(scenePath + (deferredMode ? "/rnm0comp_" + LMID + ".bin" : "/rnm0comp.bin"), FileMode.Create)); + cif.fcompRNM1 = new BinaryWriter(File.Open(scenePath + (deferredMode ? "/rnm1comp_" + LMID + ".bin" : "/rnm1comp.bin"), FileMode.Create)); + cif.fcompRNM2 = new BinaryWriter(File.Open(scenePath + (deferredMode ? "/rnm2comp_" + LMID + ".bin" : "/rnm2comp.bin"), FileMode.Create)); + + if (clientMode) + { + ftClient.serverFileList.Add("rnm0comp_" + LMID + ".bin"); + ftClient.serverFileList.Add("rnm1comp_" + LMID + ".bin"); + ftClient.serverFileList.Add("rnm2comp_" + LMID + ".bin"); + } + + if (bounces > 0) + { + cif.fcompRNM0.Write(lmname + "_final_RNM0" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompRNM1.Write(lmname + "_final_RNM1" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompRNM2.Write(lmname + "_final_RNM2" + (compressedOutput ? ".lz4" : ".dds")); + } + } + if (shMode) + { + cif.fcompSH = new BinaryWriter(File.Open(scenePath + (deferredMode ? "/shcomp_" + LMID + ".bin" : "/shcomp.bin"), FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("shcomp_" + LMID + ".bin"); + if (bounces > 0) + { + cif.fcompSH.Write(lmname + "_final_RNM0" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lmname + "_final_RNM1" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lmname + "_final_RNM2" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lmname + "_final_RNM3" + (compressedOutput ? ".lz4" : ".dds")); + } + if (currentGroup.computeSSS) + { + cif.fcompSH.Write(lmname + "_SSS_RNM0" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lmname + "_SSS_RNM1" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lmname + "_SSS_RNM2" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lmname + "_SSS_RNM3" + (compressedOutput ? ".lz4" : ".dds")); + } + } + } + fcompStartPos = cif.fcomp.BaseStream.Position; + + // Area lights + for(int i=0; i(); + if (lmr == null && lma == null) continue; + + if (lma != null && ftLightMeshInspector.IsArea(lma)) + { + lmr = null; + } + else + { + lma = null; + } + + Bounds lBounds; + Vector3[] corners = null; + if (lma != null) + { + corners = ftLightMeshInspector.GetAreaLightCorners(lma); + lBounds = new Bounds(corners[0], Vector3.zero); + lBounds.Encapsulate(corners[1]); + lBounds.Encapsulate(corners[2]); + lBounds.Encapsulate(corners[3]); + } + else + { + var lmrState = lmr.enabled; + lmr.enabled = true; + lBounds = lmr.bounds; + lmr.enabled = lmrState; + } + + lBounds.Expand(new Vector3(obj.cutoff, obj.cutoff, obj.cutoff)); + if (!lBounds.Intersects(storage.bounds[LMID])) continue; + + var lname = GetLightName(obj.gameObject, LMID); + if (doCompose) + { + cif.fcomp.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + if (bounces > 0) + { + cif.fcomp.Write(obj.indirectIntensity * hackIndirectBoost); + if (Mathf.Abs(obj.indirectIntensity - 1.0f) > 0.01f) usesIndirectIntensity = true; + } + + if ((rmode == (int)RenderMode.Indirect || rmode == (int)RenderMode.Shadowmask) + && obj.bakeToIndirect) + { + cif.fcompIndirect.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + if (cif.fcompDirIndirect != null) + { + cif.fcompDirIndirect.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompDirIndirect.Write(lname + "_Dir" + (compressedOutput ? ".lz4" : ".dds")); + } + } + } + + string renderMode; + int passes = PASS_HALF; + if (dominantDirMode && (rmodeFullLight || obj.bakeToIndirect)) + { + renderMode = obj.texture == null ? "arealightdir" : "texarealightdir"; + passes |= PASS_DIRECTION; + + cif.fcompDir.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompDir.Write(lname + "_Dir" + (compressedOutput ? ".lz4" : ".dds")); + } + else if (rnmMode && (rmodeFullLight || obj.bakeToIndirect)) + { + renderMode = obj.texture == null ? "arealightrnm" : "texarealightrnm"; + if (bounces == 0) passes = 0; + passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2; + + cif.fcompRNM0.Write(lname + "_RNM0" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompRNM1.Write(lname + "_RNM1" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompRNM2.Write(lname + "_RNM2" + (compressedOutput ? ".lz4" : ".dds")); + } + else if (shMode && (rmodeFullLight || obj.bakeToIndirect)) + { + if (shModeProbe) { + renderMode = obj.texture == null ? "arealightprobesh" : "texarealightprobesh"; + } else { + renderMode = obj.texture == null ? "arealightsh" : "texarealightsh"; + } + if (bounces == 0) passes = 0; + passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2 | PASS_RNM3; + + cif.fcompSH.Write(lname + "_RNM0" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM1" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM2" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM3" + (compressedOutput ? ".lz4" : ".dds")); + } + else + { + renderMode = obj.texture == null ? "arealight" : "texarealight"; + } + + if (!performRendering) continue; + + ftBuildLights.BuildLight(obj, SampleCount(obj.samples), corners, deferredMode ? ("lights" + i + ".bin") : "lights.bin"); + + + var pth = scenePath + "/" + lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds"); + if (!IsLightDirty(obj) && File.Exists(pth)) continue;// && new FileInfo(pth).Length == 128+size*size*8) continue; + + string progressText = "Rendering area light " + obj.name + " for " + lmname + "..."; + if (!deferredMode) ProgressBarShow(progressText, (progressStepsDone / (float)progressSteps), true); + if (userCanceled) + { + if (doCompose) + { + cif.fcomp.Close(); + if (cif.fcompIndirect != null) cif.fcompIndirect.Close(); + if (cif.fcompDirIndirect != null) cif.fcompDirIndirect.Close(); + if (cif.fcompDir != null) cif.fcompDir.Close(); + if (cif.fcompRNM0 != null) cif.fcompRNM0.Close(); + if (cif.fcompRNM1 != null) cif.fcompRNM1.Close(); + if (cif.fcompRNM2 != null) cif.fcompRNM2.Close(); + if (cif.fcompSH != null) cif.fcompSH.Close(); + } + yield break; + } + yield return null; + + DebugLogInfo("Preparing light " + obj.name + "..."); + + int errCode = 0; + if (exeMode) + { + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + /*if (userRenderMode == RenderMode.Shadowmask && obj.shadowmask) + { + passes |= PASS_MASK; + }*/ + startInfo.Arguments = renderMode + " " + scenePathQuoted + " \"" + lname + "\" " + passes + " " + 0 + " " + LMID + " lights" + i + ".bin"; + + if (deferredMode) + { + deferredFileSrc.Add("");//scenePath + "/lights" + i + ".bin"); + deferredFileDest.Add("");//scenePath + "/lights.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + } + else + { + /*Debug.Log("Running ftrace " + startInfo.Arguments); + exeProcess = System.Diagnostics.Process.Start(startInfo); + exeProcess.WaitForExit(); + errCode = exeProcess.ExitCode;*/ + } + } + if (errCode != 0) + { + DebugLogError("ftrace error: " + ftErrorCodes.TranslateFtrace(errCode, rtxMode)); + userCanceled = true; + if (doCompose) + { + cif.fcomp.Close(); + if (cif.fcompIndirect != null) cif.fcompIndirect.Close(); + if (cif.fcompDirIndirect != null) cif.fcompDirIndirect.Close(); + if (cif.fcompDir != null) cif.fcompDir.Close(); + if (cif.fcompRNM0 != null) cif.fcompRNM0.Close(); + if (cif.fcompRNM1 != null) cif.fcompRNM1.Close(); + if (cif.fcompRNM2 != null) cif.fcompRNM2.Close(); + if (cif.fcompSH != null) cif.fcompSH.Close(); + } + yield break;//return false; + } + + //StoreLight(obj); + } + + // Point lights + int start = 0; + int end = 0; + int sampleCounter = 0; + int channel = -1; + bool bakeToIndirect = false; + float indirectIntensity = 1.0f; + bool[] skipLight = null; + int addedLights = 0; + if (ftAdditionalConfig.batchPointLights) + { + if (AllP.Length > 0) + { + channel = GetShadowmaskChannel(AllP[0]); + bakeToIndirect = AllP[0].bakeToIndirect; + indirectIntensity = AllP[0].indirectIntensity; + } + skipLight = new bool[AllP.Length]; + } + for(int i=0; i().range * 2; + var lBounds = new Bounds(obj.transform.position, new Vector3(boundsRange, boundsRange, boundsRange)); + if (!lBounds.Intersects(storage.bounds[LMID])) continue; + + string lname = ""; + string settingsFile = ""; + + // Split in batches if needed + bool bakeBatch = false; + if (ftAdditionalConfig.batchPointLights) + { + skipLight[i] = false; + addedLights++; + bool split = false; + + // Split by bakeToIndirect + if (AllP[i].bakeToIndirect != bakeToIndirect) + { + split = true; + bakeToIndirect = AllP[i].bakeToIndirect; + } + + // Split by indirectIntensity + if (AllP[i].indirectIntensity != indirectIntensity) + { + split = true; + indirectIntensity = AllP[i].indirectIntensity; + } + + // Split by shadowmask channel + var objChannel = GetShadowmaskChannel(AllP[i]); + if (objChannel != channel) + { + split = true; + channel = objChannel; + } + + // Split by count + int newSampleCount = sampleCounter + AllP[i].samples; + if (newSampleCount > maxSamplesPerPointLightBatch) + { + split = true; + sampleCounter = 0; + } + sampleCounter += AllP[i].samples; + + if (split) + { + end = i-1; + lname = PrepareBatchPointLight(start, end, LMID, skipLight, cif, rmode, dominantDirMode, rnmMode, shMode, ref usesIndirectIntensity); + settingsFile = "batchpointlight_" + LMID + "_" + start + "_" + end + ".bin"; + bakeBatch = true; + } + } + else + { + // Update shadowmask settings for LMGroup + lname = GetLightName(obj.gameObject, LMID); + UpdateLightmapShadowmaskFromPointLight(obj, LMID, lname, lmname); + + // Update composing instructions + if (WriteCompFiles(obj, cif, lname, rmode, dominantDirMode, rnmMode, shMode)) usesIndirectIntensity = true; + + settingsFile = "pointlight" + i + ".bin"; + } + + if (!performRendering) continue; + + if (ftAdditionalConfig.batchPointLights) + { + if (bakeBatch) + { + // Export batch light data and textures + bool isError = ftBuildLights.BuildLights(AllP, start, end, skipLight, sampleDivisor, false, settingsFile); // TODO: dirty tex detection!! + if (isError) + { + userCanceled = true; + cif.Close(); + yield break; + } + + // Cancel + if (userCanceled) + { + cif.Close(); + yield break; + } + yield return null; + + // Generate batch command line + string renderMode = "batchpointlight"; + string progressText = "Rendering point light batch (" + (start) + "-" + (end) + ") for " + lmname + "..."; + AddPointLightCommandLine(renderMode, lname, settingsFile, progressText, LMID, AllP[start], rmode, dominantDirMode, rnmMode, shMode, shModeProbe); + + start = i; + } + } + else + { + // Export light data and textures + bool isError = ftBuildLights.BuildLight(obj, SampleCount(obj.samples), true, false, settingsFile); // TODO: dirty tex detection!! + if (isError) + { + userCanceled = true; + cif.Close(); + yield break; + } + if (obj.projMode != 0) + { + //yield return new WaitForEndOfFrame(); + //yield return new WaitForSeconds(1); // ????? + yield return null; + } + + // Check if "update unmodified lights" is off, and this light was modified + var pth = scenePath + "/" + lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds"); + if (!IsLightDirty(obj) && File.Exists(pth)) continue;// && new FileInfo(pth).Length == 128+size*size*8) continue; + + // Get ftrace rendermode + string renderMode = GetPointLightRenderMode(obj); + + // Progressbar + string progressText = "Rendering point light " + obj.name + " for " + lmname + "..."; + + // Cancel + if (userCanceled) + { + cif.Close(); + yield break; + } + yield return null; + + // Generate command line + AddPointLightCommandLine(renderMode, lname, settingsFile, progressText, LMID, AllP[i], rmode, dominantDirMode, rnmMode, shMode, shModeProbe); + } + } + if (ftAdditionalConfig.batchPointLights && addedLights > 0) + { + end = AllP.Length-1; + string lname = PrepareBatchPointLight(start, end, LMID, skipLight, cif, rmode, dominantDirMode, rnmMode, shMode, ref usesIndirectIntensity); + string settingsFile = "batchpointlight_" + LMID + "_" + start + "_" + end + ".bin"; + string renderMode = "batchpointlight"; + string progressText = "Rendering point light batch (" + (start) + "-" + (end) + ") for " + lmname + "..."; + bool isError = ftBuildLights.BuildLights(AllP, start, end, skipLight, sampleDivisor, false, settingsFile); // TODO: dirty tex detection!! + if (isError) + { + userCanceled = true; + cif.Close(); + yield break; + } + AddPointLightCommandLine(renderMode, lname, settingsFile, progressText, LMID, AllP[start], rmode, dominantDirMode, rnmMode, shMode, shModeProbe); + } + + // Skylight + for(int i=0; i 0) + { + cif.fcomp.Write(obj.indirectIntensity * hackIndirectBoost); + if (Mathf.Abs(obj.indirectIntensity - 1.0f) > 0.01f) usesIndirectIntensity = true; + } + + if ((rmode == (int)RenderMode.Indirect || rmode == (int)RenderMode.Shadowmask) + && obj.bakeToIndirect) + { + cif.fcompIndirect.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + if (cif.fcompDirIndirect != null) + { + cif.fcompDirIndirect.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompDirIndirect.Write(lname + "_Dir" + (compressedOutput ? ".lz4" : ".dds")); + } + } + + if (dominantDirMode && (rmodeFullLight || obj.bakeToIndirect)) + { + cif.fcompDir.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompDir.Write(lname + "_Dir" + (compressedOutput ? ".lz4" : ".dds")); + } + else if (rnmMode && (rmodeFullLight || obj.bakeToIndirect)) + { + cif.fcompRNM0.Write(lname + "_RNM0" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompRNM1.Write(lname + "_RNM1" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompRNM2.Write(lname + "_RNM2" + (compressedOutput ? ".lz4" : ".dds")); + } + else if (shMode && (rmodeFullLight || obj.bakeToIndirect)) + { + cif.fcompSH.Write(lname + "_RNM0" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM1" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM2" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM3" + (compressedOutput ? ".lz4" : ".dds")); + } + } + + if (!performRendering) continue; + + /* + if (!storage.skylights.Contains(obj)) + { + storage.skylights.Add(obj); + storage.skylightsDirty.Add(true); + } + var skylightIndex = storage.skylights.IndexOf(obj); + */ + var texDirty = obj.cubemap != null;//true;//storage.skylightsDirty[skylightIndex]; + + ftBuildLights.BuildSkyLight(obj, SampleCount(obj.samples), texDirty, deferredMode ? "sky" + i + ".bin" : "sky.bin"); + + if (texDirty) + { + //yield return new WaitForEndOfFrame(); + yield return new WaitForSeconds(1); + } + + //storage.skylightsDirty[skylightIndex] = false; + + var pth = scenePath + "/" + lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds"); + if (!IsLightDirty(obj) && File.Exists(pth)) continue;// && new FileInfo(pth).Length == 128+size*size*8) continue; + + string progressText = "Rendering sky light " + obj.name + " for " + lmname + "..."; + if (!deferredMode) ProgressBarShow(progressText, (progressStepsDone / (float)progressSteps), true); + if (userCanceled) + { + if (doCompose) + { + cif.fcomp.Close(); + if (cif.fcompIndirect != null) cif.fcompIndirect.Close(); + if (cif.fcompDirIndirect != null) cif.fcompDirIndirect.Close(); + if (cif.fcompDir != null) cif.fcompDir.Close(); + if (cif.fcompRNM0 != null) cif.fcompRNM0.Close(); + if (cif.fcompRNM1 != null) cif.fcompRNM1.Close(); + if (cif.fcompRNM2 != null) cif.fcompRNM2.Close(); + if (cif.fcompSH != null) cif.fcompSH.Close(); + } + yield break; + } + yield return null; + + var bakeDir = (dominantDirMode && (rmodeFullLight || obj.bakeToIndirect)); + var bakeRNM = (rnmMode && (rmodeFullLight || obj.bakeToIndirect)); + var bakeSH = (shMode && (rmodeFullLight || obj.bakeToIndirect)); + string renderMode; + if (obj.cubemap != null) + { + if (bakeDir) + { + renderMode = "skycubemapdir"; + } + else if (bakeRNM) + { + renderMode = "skycubemaprnm"; + } + else if (bakeSH) + { + renderMode = shModeProbe ? "skycubemapprobesh" : "skycubemapsh"; + } + else + { + renderMode = "skycubemap"; + } + } + else + { + if (bakeDir) + { + renderMode = "skydir"; + } + else if (bakeRNM) + { + renderMode = "skyrnm"; + } + else if (bakeSH) + { + renderMode = obj.tangentSH ? "skytangentsh" : (shModeProbe ? "skyprobesh" : "skysh"); + } + else + { + renderMode = "sky"; + } + } + + int errCode = 0; + if (exeMode) + { + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + int passes = PASS_HALF; + if (bakeDir) passes |= PASS_DIRECTION; + if ((bakeRNM || bakeSH) && bounces == 0) passes = 0; + if (bakeRNM) passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2; + if (bakeSH) passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2 | PASS_RNM3; + startInfo.Arguments = renderMode + " " + scenePathQuoted + " \"" + lname + "\" " + passes + " " + 0 + " " + LMID + " sky" + i + ".bin"; + + if (deferredMode) + { + deferredFileSrc.Add("");//scenePath + "/sky" + i + ".bin"); + deferredFileDest.Add("");//scenePath + "/sky.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + } + else + { + /*Debug.Log("Running ftrace " + startInfo.Arguments); + exeProcess = System.Diagnostics.Process.Start(startInfo); + exeProcess.WaitForExit(); + errCode = exeProcess.ExitCode;*/ + } + } + + if (errCode != 0) + { + DebugLogError("ftrace error: "+ftErrorCodes.TranslateFtrace(errCode, rtxMode)); + userCanceled = true; + if (doCompose) + { + cif.fcomp.Close(); + if (cif.fcompIndirect != null) cif.fcompIndirect.Close(); + if (cif.fcompDirIndirect != null) cif.fcompDirIndirect.Close(); + if (cif.fcompDir != null) cif.fcompDir.Close(); + if (cif.fcompRNM0 != null) cif.fcompRNM0.Close(); + if (cif.fcompRNM1 != null) cif.fcompRNM1.Close(); + if (cif.fcompRNM2 != null) cif.fcompRNM2.Close(); + if (cif.fcompSH != null) cif.fcompSH.Close(); + } + yield break; + } + //StoreLight(obj); + } + + // Directional light + for(int i=0; i(); + if (ulht == null) + { + DebugLogWarning("Light " + obj.name + " set to shadowmask, but doesn't have real-time light");; + } + else + { + UpdateMaskArray(currentGroup.id, lname, lmname, ulht, obj.shadowmaskDenoise); + } + } + + if (doCompose) + { + var texName = lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds"); + cif.fcomp.Write(texName); + if (bounces > 0) + { + cif.fcomp.Write(obj.indirectIntensity * hackIndirectBoost); + if (Mathf.Abs(obj.indirectIntensity - 1.0f) > 0.01f) usesIndirectIntensity = true; + } + + if ((rmode == (int)RenderMode.Indirect || rmode == (int)RenderMode.Shadowmask) + && obj.bakeToIndirect) + { + cif.fcompIndirect.Write(texName); + if (cif.fcompDirIndirect != null) + { + cif.fcompDirIndirect.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompDirIndirect.Write(lname + "_Dir" + (compressedOutput ? ".lz4" : ".dds")); + } + } + + if (dominantDirMode && (rmodeFullLight || obj.bakeToIndirect)) + { + cif.fcompDir.Write(lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompDir.Write(lname + "_Dir" + (compressedOutput ? ".lz4" : ".dds")); + } + else if (rnmMode && (rmodeFullLight || obj.bakeToIndirect)) + { + cif.fcompRNM0.Write(lname + "_RNM0" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompRNM1.Write(lname + "_RNM1" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompRNM2.Write(lname + "_RNM2" + (compressedOutput ? ".lz4" : ".dds")); + } + else if (shMode && (rmodeFullLight || obj.bakeToIndirect)) + { + cif.fcompSH.Write(lname + "_RNM0" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM1" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM2" + (compressedOutput ? ".lz4" : ".dds")); + cif.fcompSH.Write(lname + "_RNM3" + (compressedOutput ? ".lz4" : ".dds")); + } + } + + if (!performRendering) continue; + + ftBuildLights.BuildDirectLight(obj, SampleCount(obj.samples), false, deferredMode ? "direct" + i + ".bin" : "direct.bin"); + + if (hasAnyVolumes) + { + ftBuildLights.BuildDirectLight(obj, SampleCount(obj.samples), true, deferredMode ? "direct" + i + "_volumes.bin" : "direct.bin"); + } + + var pth = scenePath + "/" + lname + "_HDR" + (compressedOutput ? ".lz4" : ".dds"); + if (!IsLightDirty(obj) && File.Exists(pth)) continue;// && new FileInfo(pth).Length == 128+size*size*8) continue; + //Debug.Log(IsLightDirty(obj)+" "+File.Exists(pth)+" "+(new FileInfo(pth).Length == 128+size*size*8)); + + string progressText = "Rendering direct light " + obj.name + " for " + lmname + "..."; + if (!deferredMode) ProgressBarShow(progressText, (progressStepsDone / (float)progressSteps), true); + if (userCanceled) + { + if (doCompose) + { + cif.fcomp.Close(); + if (cif.fcompIndirect != null) cif.fcompIndirect.Close(); + if (cif.fcompDirIndirect != null) cif.fcompDirIndirect.Close(); + if (cif.fcompDir != null) cif.fcompDir.Close(); + if (cif.fcompRNM0 != null) cif.fcompRNM0.Close(); + if (cif.fcompRNM1 != null) cif.fcompRNM1.Close(); + if (cif.fcompRNM2 != null) cif.fcompRNM2.Close(); + if (cif.fcompSH != null) cif.fcompSH.Close(); + } + yield break; + } + yield return null; + + int errCode = 0; + if (exeMode) + { + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + + int passes = PASS_HALF; + string rrmode = GetSunRenderMode(obj); + if (dominantDirMode && (rmodeFullLight || obj.bakeToIndirect)) + { + passes |= PASS_DIRECTION; + } + else if (rnmMode && (rmodeFullLight || obj.bakeToIndirect)) + { + rrmode += "rnm"; + if (bounces == 0) passes = 0; + passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2; + } + else if (shMode && (rmodeFullLight || obj.bakeToIndirect)) + { + rrmode += shModeProbe ? "probesh" : "sh"; + if (bounces == 0) passes = 0; + passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2 | PASS_RNM3; + } + if (userRenderMode == RenderMode.Shadowmask && obj.shadowmask) + { + passes |= PASS_MASK; + if (currentGroup.transparentSelfShadow) passes |= PASS_MASK1; + } + + startInfo.Arguments = rrmode + " " + scenePathQuoted + " \"" + lname + "\" " + passes + " " + 0 + " " + LMID + + " direct" + i + ((currentGroup.probes && currentGroup.name == "volumes") ? "_volumes" : "") + ".bin"; + + deferredFileSrc.Add("");//scenePath + "/direct" + i + ".bin"); + deferredFileDest.Add("");//scenePath + "/direct.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + } + + if (errCode != 0) + { + DebugLogError("ftrace error: "+ftErrorCodes.TranslateFtrace(errCode, rtxMode)); + userCanceled = true; + if (doCompose) + { + cif.fcomp.Close(); + if (cif.fcompIndirect != null) cif.fcompIndirect.Close(); + if (cif.fcompDirIndirect != null) cif.fcompDirIndirect.Close(); + if (cif.fcompDir != null) cif.fcompDir.Close(); + if (cif.fcompRNM0 != null) cif.fcompRNM0.Close(); + if (cif.fcompRNM1 != null) cif.fcompRNM1.Close(); + if (cif.fcompRNM2 != null) cif.fcompRNM2.Close(); + if (cif.fcompSH != null) cif.fcompSH.Close(); + } + yield break;//return false; + } + //StoreLight(obj); + } + + lmnameComposed[lmname] = true; + + if (dominantDirMode && cif.fcompDir.BaseStream.Position > 0) + { + lightmapHasDir[LMID] = true; + } + + if (rnmMode && cif.fcompRNM0.BaseStream.Position > 0) + { + lightmapHasRNM[LMID] = true; + } + + if (shMode && cif.fcompSH.BaseStream.Position > 0) + { + lightmapHasRNM[LMID] = true; + } + + if (cif.fcomp.BaseStream.Position == fcompStartPos) + { + cif.fcomp.Write(lmname + "_lights_HDR.dds"); + + /*cif.fcomp.Close(); + if (cif.fcompIndirect != null) cif.fcompIndirect.Close();*/ + DebugLogInfo("No lights for " + lmname); + + var fpos = new BinaryWriter(File.Open(scenePath + "/" + lmname + "_lights_HDR.dds", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add(lmname + "_lights_HDR.dds"); + //var fpos = new BinaryWriter(File.Open(scenePath + "/" + lmname + "_diffuse_HDR" + (compressedOutput ? ".lz4" : ".dds"), FileMode.Create)); + fpos.Write(ftDDS.ddsHeaderHalf4); + + int atlasTexSize = resolution; + if (currentGroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) + { + atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)currentGroup.totalVertexCount)); + atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)ftRenderLightmap.tileSize) * ftRenderLightmap.tileSize; + } + + var halfs = new ushort[atlasTexSize*atlasTexSize*4]; + for(int f=0; f 0 ? "_lights_Dir" : "_final_Dir") + (compressedOutput ? ".lz4" : ".dds") + + "\" " + PASS_DIRECTION + " " + 0 + " " + LMID + " dircomp_" + LMID + ".bin"; + + if (deferredMode) + { + deferredFileSrc.Add("");//scenePath + "/dircomp_" + LMID + ".bin"); + deferredFileDest.Add("");//scenePath + "/dircomp.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText2); + } + else + { + Debug.LogError("Not supported"); + } + } + } + + if (errCode2 != 0) + { + DebugLogError("ftrace error: "+ftErrorCodes.TranslateFtrace(errCode2, rtxMode)); + userCanceled = true; + yield break; + } + } + + bool RenderLMAO(int LMID, string lmname) + { + string progressText = "Rendering AO for " + lmname + "..."; + if (!deferredMode) ProgressBarShow(progressText, (progressStepsDone / (float)progressSteps), true); + + var rmode = currentGroup.renderMode == BakeryLightmapGroup.RenderMode.Auto ? (int)userRenderMode : (int)currentGroup.renderMode; + + int passes = rmode == (int)RenderMode.AmbientOcclusionOnly ? PASS_HALF : PASS_MASK; + + // There is no realistic weight for AO to mix with other light directions + var dirMode = currentGroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)currentGroup.renderDirMode; + var dominantDirMode = dirMode == (int)ftRenderLightmap.RenderDirMode.DominantDirection; + if (dominantDirMode && rmode == (int)RenderMode.AmbientOcclusionOnly) passes |= PASS_DIRECTION; + + var fao = new BinaryWriter(File.Open(scenePath + "/ao.bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("ao.bin"); + fao.Write(SampleCount(hackAOSamples)); + fao.Write(hackAORadius); + fao.Write(rmode == (int)RenderMode.AmbientOcclusionOnly ? hackAOIntensity : 1.0f); + fao.Close(); + + System.Diagnostics.ProcessStartInfo startInfo; + //System.Diagnostics.Process exeProcess; + + int errCode = 0; + if (exeMode) + { + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + string renderMode; + if (dominantDirMode && rmode == (int)RenderMode.AmbientOcclusionOnly) + { + renderMode = "aodir"; + } + else + { + renderMode = "ao";//currentGroup.aoIsThickness ? "thickness" : "ao"; + } + + if (rmode == (int)RenderMode.AmbientOcclusionOnly) + { + startInfo.Arguments = renderMode + " " + scenePathQuoted + " \"" + lmname + "_final" + "\" " + passes + " " + 16 + " " + LMID; + } + else + { + startInfo.Arguments = renderMode + " " + scenePathQuoted + " \"" + lmname + "_ao" + "\" " + passes + " " + 16 + " " + LMID; + } + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + } + + if (errCode != 0) + { + DebugLogError("ftrace error: "+ftErrorCodes.TranslateFtrace(errCode, rtxMode)); + userCanceled = true; + return false; + } + return true; + } + + void RenderLMSSS(BakeryLightmapGroup lmgroup, bool lastPass) + { + int LMID = lmgroup.id; + + //var rmode = lmgroup.renderMode == BakeryLightmapGroup.RenderMode.Auto ? (int)userRenderMode : (int)lmgroup.renderMode; + + var dirMode = lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)lmgroup.renderDirMode; + //var dominantDirMode = dirMode == (int)ftRenderLightmap.RenderDirMode.DominantDirection && lightmapHasDir[lmgroup.id]; + var rnmMode = dirMode == (int)ftRenderLightmap.RenderDirMode.RNM && lightmapHasRNM[LMID]; + var shMode = dirMode == (int)ftRenderLightmap.RenderDirMode.SH && lightmapHasRNM[LMID]; + + int passes = PASS_HALF; + //if (dominantDirMode && lastPass) passes |= PASS_DIRECTION; + if (rnmMode && lastPass) passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2; + if (shMode && lastPass) passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2 | PASS_RNM3; + + var remode = "sss"; + /*if (dominantDirMode) + { + rmode = "sss"; + } + else if (rnmMode) + { + rmode = "sssrnm"; + } + else*/ if (shMode && lastPass) + { + remode = "ssssh"; + } + + var fsss = new BinaryWriter(File.Open(scenePath + "/sss" + LMID + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("sss.bin"); + fsss.Write(SampleCount(lmgroup.sssSamples)); + fsss.Write(lmgroup.sssDensity); + fsss.Write(Mathf.Pow(lmgroup.sssColor.r,2.2f) * lmgroup.sssScale); + fsss.Write(Mathf.Pow(lmgroup.sssColor.g,2.2f) * lmgroup.sssScale); + fsss.Write(Mathf.Pow(lmgroup.sssColor.b,2.2f) * lmgroup.sssScale); + fsss.Close(); + + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = remode + " " + scenePathQuoted + " \"" + lmgroup.name + (lastPass ? "_SSS" : "_diffuse") + + "\"" + " " + passes + " " + 0 + " " + lmgroup.id + + " sss" + LMID + ".bin" + + " \"" + lmgroup.name + "_diffuse_HDR" + (compressedOutput ? ".lz4" : ".dds") + "\""; // full lighting passed as direct + + deferredFileSrc.Add("");//scenePath + "/sss" + LMID + ".bin"); + deferredFileDest.Add("");//scenePath + "/sss.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add("Computing subsurface scattering for " + lmgroup.name + "..."); + } + + bool RenderLMGI(int LMID, string lmname, int i, bool needsGIPass, bool lastPass) + { + string progressText = "Rendering GI bounce " + i + " for " + lmname + "..."; + if (!deferredMode) ProgressBarShow(progressText, (progressStepsDone / (float)progressSteps), true); + + var dirMode = currentGroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)currentGroup.renderDirMode; + var dominantDirMode = dirMode == (int)ftRenderLightmap.RenderDirMode.DominantDirection && lightmapHasDir[LMID]; + var rnmMode = dirMode == (int)ftRenderLightmap.RenderDirMode.RNM && lightmapHasRNM[LMID]; + var shMode = dirMode == (int)ftRenderLightmap.RenderDirMode.SH && lightmapHasRNM[LMID]; + var shModeProbe = dirMode == (int)BakeryLightmapGroup.RenderDirMode.ProbeSH && lightmapHasRNM[LMID]; + if (shModeProbe) shMode = true; + + // Needs both HALF and SECONDARY_HALF because of multiple lightmaps reading each other's lighting + int passes = needsGIPass ? (PASS_HALF|PASS_SECONDARY_HALF) : PASS_HALF; + + if (dominantDirMode && lastPass) passes |= PASS_DIRECTION; + if (rnmMode && lastPass) passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2; + if (shMode && lastPass) passes |= PASS_RNM0 | PASS_RNM1 | PASS_RNM2 | PASS_RNM3; + + System.Diagnostics.ProcessStartInfo startInfo; + //System.Diagnostics.Process exeProcess; + + int errCode = 0; + if (exeMode) + { + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + string rmode = "texgi"; + if (dominantDirMode && lastPass) + { + rmode = "texgidir"; + } + else if (rnmMode && lastPass) + { + rmode = "texgirnm"; + } + else if (shMode && lastPass) + { + rmode = shModeProbe ? "texgiprobesh" : "texgish"; + } + startInfo.Arguments = rmode + " " + scenePathQuoted + " \"" + lmname + (i==bounces-1 ? "_final" : "_diffuse") + "\" " + passes + " " + 16 + " " + LMID; + startInfo.Arguments += " \"gi_" + lmname + i + ".bin\""; + if (i == bounces-1) + { + // add direct lighting on top of GI + startInfo.Arguments += " \"" + lmname + "_lights_HDR" + (compressedOutput ? ".lz4" : ".dds") + "\""; // direct lighting + } + else + { + // add direct*albedo+emissive on top of GI + startInfo.Arguments += " \"" + lmname + "_diffuse0_HDR" + (compressedOutput ? ".lz4" : ".dds") + "\""; // direct lighting + } + + if (giLodModeEnabled) + { + startInfo.Arguments += " vbTraceTex" + LMID + ".bin"; + } + else + { + startInfo.Arguments += " vbTraceTex.bin"; + } + + deferredFileSrc.Add("");//scenePath + "/gi_" + lmname + i + ".bin"); + deferredFileDest.Add("");//scenePath + "/gi.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + } + + if (errCode != 0) + { + DebugLogError("ftrace error: "+ftErrorCodes.TranslateFtrace(errCode, rtxMode)); + userCanceled = true; + return false; + } + return true; + } + + void UpdateMaskArray(int LMID, string lname, string lmname, Light ulht, bool denoise) + { +#if UNITY_2017_3_OR_NEWER + var maskChannel = ulht.bakingOutput.occlusionMaskChannel; +#else + var so = new SerializedObject(ulht); + var maskChannel = so.FindProperty("m_BakingOutput").FindPropertyRelative("occlusionMaskChannel").intValue; +#endif + if (maskChannel >=0 && maskChannel <= 3) + { + var maskArray = lightmapMasks[LMID]; + var maskArrayLMNames = lightmapMaskLMNames[LMID]; + var maskArrayLights = lightmapMaskLights[LMID]; + var maskArrayDenoise = lightmapMaskDenoise[LMID]; + while(maskArray.Count < maskChannel + 1) + { + maskArray.Add(new List()); + maskArrayLMNames.Add(new List()); + maskArrayLights.Add(new List()); + maskArrayDenoise.Add(new List()); + } + maskArray[maskChannel].Add(lname + "_Mask" + (compressedOutput ? ".lz4" : ".dds")); + maskArrayLMNames[maskChannel].Add(lmname); + maskArrayLights[maskChannel].Add(ulht); + maskArrayDenoise[maskChannel].Add(denoise); + lightmapHasMask[LMID] = true; + } + } + + bool SetupLightShadowmask(Light light, int channel) + { + bool success = true; + if (channel > 3) + { + success = false; + DebugLogWarning("Light " + light.name + " can't generate shadow mask (out of channels)."); + overlappingLights.Add(light.gameObject); + } + + int occlusionMaskChannel = channel > 3 ? -1 : channel; + +#if UNITY_2017_3_OR_NEWER + var output = new LightBakingOutput(); + output.isBaked = true; + output.lightmapBakeType = LightmapBakeType.Mixed; + output.mixedLightingMode = userRenderMode == RenderMode.Shadowmask ? MixedLightingMode.Shadowmask : MixedLightingMode.Subtractive; + output.occlusionMaskChannel = occlusionMaskChannel; + output.probeOcclusionLightIndex = light.bakingOutput.probeOcclusionLightIndex; + light.bakingOutput = output; +#else + light.alreadyLightmapped = true; + light.lightmapBakeType = LightmapBakeType.Mixed; + var so = new SerializedObject(light); + var sp = so.FindProperty("m_BakingOutput"); + sp.FindPropertyRelative("occlusionMaskChannel").intValue = occlusionMaskChannel; + //sp.FindPropertyRelative("probeOcclusionLightIndex").intValue = -1; + sp.FindPropertyRelative("lightmappingMask").intValue = -1; + so.ApplyModifiedProperties(); + + if (!maskedLights.Contains(light)) maskedLights.Add(light); + +#endif + + var st = storages[light.gameObject.scene]; + if (!st.bakedLights.Contains(light)) + { + st.bakedLights.Add(light); + st.bakedLightChannels.Add(occlusionMaskChannel); + } + + return success; + } + + void PrepareAssetImporting() + { + var outputPathCompat = outputPathFull.Replace("\\", "/"); + + // Prepare asset importing: set AssetPostprocessor settings + ftTextureProcessor.texSettings = new Dictionary(); + foreach(var lmgroup in groupListGIContributingPlain) + { + if (lmgroup.vertexBake) continue; + var nm = lmgroup.name; + + int colorSize = lmgroup.resolution / (1 << (int)((1.0f - ftBuildGraphics.mainLightmapScale) * 6)); + int maskSize = lmgroup.resolution / (1 << (int)((1.0f - ftBuildGraphics.maskLightmapScale) * 6)); + int dirSize = lmgroup.resolution / (1 << (int)((1.0f - ftBuildGraphics.dirLightmapScale) * 6)); + + var dirMode = lmgroup.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)lmgroup.renderDirMode; + var dominantDirMode = dirMode == (int)ftRenderLightmap.RenderDirMode.DominantDirection && lightmapHasDir[lmgroup.id]; + var rnmMode = dirMode == (int)ftRenderLightmap.RenderDirMode.RNM && lightmapHasRNM[lmgroup.id]; + var shMode = dirMode == (int)ftRenderLightmap.RenderDirMode.SH && lightmapHasRNM[lmgroup.id]; + var shModeProbe = dirMode == (int)BakeryLightmapGroup.RenderDirMode.ProbeSH && lightmapHasRNM[lmgroup.id]; + if (shModeProbe) shMode = true; + + //if (!bc6h) + { + //if (File.Exists(folder + "../Assets/" + nm + "_final.hdr")) + { + //var outfile = "Assets/"+nm+"_final_RGBM.dds"; + //Texture2D lm = null; + var outfile = "Assets/" + outputPathCompat + "/"+nm+"_final.hdr"; + if (rnmMode) outfile = "Assets/" + outputPathCompat + "/"+nm+"_RNM0.hdr"; + var desiredTextureType = encodeMode == 0 ? ftTextureProcessor.TEX_LM : ftTextureProcessor.TEX_LMDEFAULT; + if (lightmapHasColor[lmgroup.id])// && File.Exists(outfile)) + { + ftTextureProcessor.texSettings[outfile] = new Vector2(colorSize, desiredTextureType); + } + + //Texture2D mask = null; + //if (userRenderMode == RenderMode.Shadowmask && lightmapMasks[lmgroup.id].Count > 0) + if (lightmapHasMask[lmgroup.id]) + { + outfile = "Assets/" + outputPathCompat + "/"+nm+"_mask" + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga"); + desiredTextureType = ftTextureProcessor.TEX_MASK;// TextureImporterType.Default; + ftTextureProcessor.texSettings[outfile] = new Vector2(maskSize, desiredTextureType); + } + + //Texture2D dirLightmap = null; + if (dominantDirMode) + { + outfile = "Assets/" + outputPathCompat + "/"+nm+"_dir" + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga"); + desiredTextureType = ftTextureProcessor.TEX_DIR;// TextureImporterType.Default; + ftTextureProcessor.texSettings[outfile] = new Vector2(dirSize, desiredTextureType); + } + + //Texture2D rnmLightmap0 = null; + //Texture2D rnmLightmap1 = null; + //Texture2D rnmLightmap2 = null; + if (rnmMode) + { + desiredTextureType = encodeMode == 0 ? ftTextureProcessor.TEX_LM : ftTextureProcessor.TEX_LMDEFAULT; + //TextureImporterType.Lightmap : TextureImporterType.Default; + for(int c=0; c<3; c++) + { + outfile = "Assets/" + outputPathCompat + "/"+nm+"_RNM" + c + ".hdr"; + ftTextureProcessor.texSettings[outfile] = new Vector2(dirSize, desiredTextureType); + } + } + + if (shMode) + { + outfile = "Assets/" + outputPathCompat + "/"+nm+"_L0.hdr"; + desiredTextureType = encodeMode == 0 ? ftTextureProcessor.TEX_LM : ftTextureProcessor.TEX_LMDEFAULT; + ftTextureProcessor.texSettings[outfile] = new Vector2(colorSize, desiredTextureType); + + desiredTextureType = ftTextureProcessor.TEX_DIR;// TextureImporterType.Default; + for(int c=0; c<3; c++) + { + string comp; + if (c==0) + { + comp = "x"; + } + else if (c==1) + { + comp = "y"; + } + else + { + comp = "z"; + } + outfile = "Assets/" + outputPathCompat + "/"+nm+"_L1" + comp + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga"); + ftTextureProcessor.texSettings[outfile] = new Vector2(dirSize, desiredTextureType); + } + } + } + } + } + } + + IEnumerator RenderLMAddBuckets(int LMID, string lmname, int resolution, bool vertexBake, int lmgroupRenderDirMode, int lmgroupRenderMode) + { + var dirMode = lmgroupRenderDirMode == (int)BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)lmgroupRenderDirMode; + var dominantDirMode = dirMode == (int)ftRenderLightmap.RenderDirMode.DominantDirection; + var rnmMode = dirMode == (int)ftRenderLightmap.RenderDirMode.RNM && lightmapHasRNM[LMID]; + var shMode = dirMode == (int)ftRenderLightmap.RenderDirMode.SH && lightmapHasRNM[LMID]; + var shModeProbe = dirMode == (int)BakeryLightmapGroup.RenderDirMode.ProbeSH && lightmapHasRNM[LMID]; + if (shModeProbe) shMode = true; + var shadowmask = (userRenderMode == RenderMode.Shadowmask); + + if (rnmMode) + { + for(int c=0; c<3; c++) + { + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + #if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; + #endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "postadd " + scenePathQuoted + " \"" + lmname + "_final_RNM" + c + (compressedOutput ? ".lz4" : ".dds") + + "\"" + " " + PASS_HALF + " " + 0 + " " + LMID + " comp_addbuckets" + c + "_" + LMID + ".bin"; + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add("Compositing lightmaps for " + lmname + "..."); + + var fcomp = new BinaryWriter(File.Open(scenePath + "/comp_addbuckets" + c + "_" + LMID + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("comp_addbuckets" + c + "_" + LMID + ".bin"); + fcomp.Write(lmname + "_final_RNM" + c + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write("uvnormal_" + lmname + (compressedGBuffer ? ".lz4" : ".dds")); + foreach(var lmgroup in groupListPlain) + { + if (lmgroup.parentName != lmname) continue; + fcomp.Write(lmgroup.name + "_final_RNM" + c + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write("uvnormal_" + lmgroup.name + (compressedGBuffer ? ".lz4" : ".dds")); + } + fcomp.Close(); + } + } + else if (shMode) + { + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + #if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; + #endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "postadd " + scenePathQuoted + " \"" + lmname + "_final_L0" + (compressedOutput ? ".lz4" : ".dds") + + "\"" + " " + PASS_HALF + " " + 0 + " " + LMID + " comp_addbucketsL0_" + LMID + ".bin"; + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add("Compositing lightmaps for " + lmname + "..."); + + var fcomp = new BinaryWriter(File.Open(scenePath + "/comp_addbucketsL0_" + LMID + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("comp_addbucketsL0_" + LMID + ".bin"); + fcomp.Write(lmname + "_final_L0" + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write("uvnormal_" + lmname + (compressedGBuffer ? ".lz4" : ".dds")); + foreach(var lmgroup in groupListPlain) + { + if (lmgroup.parentName != lmname) continue; + fcomp.Write(lmgroup.name + "_final_L0" + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write("uvnormal_" + lmgroup.name + (compressedGBuffer ? ".lz4" : ".dds")); + } + fcomp.Close(); + + for(int c=0; c<3; c++) + { + string cname; + switch(c) + { + case 0: + cname = "L1x"; + break; + case 1: + cname = "L1y"; + break; + default: + cname = "L1z"; + break; + } + + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + #if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; + #endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "postdiradd " + scenePathQuoted + " \"" + lmname + "_final_" + cname + (compressedOutput ? ".lz4" : ".dds") + + "\"" + " " + PASS_DIRECTION + " " + 0 + " " + LMID + " dircomp_addbuckets" + c + "_" + LMID + ".bin"; + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add("Compositing directions for " + lmname + "..."); + + fcomp = new BinaryWriter(File.Open(scenePath + "/dircomp_addbuckets" + c + "_" + LMID + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("dircomp_addbuckets" + c + "_" + LMID + ".bin"); + fcomp.Write(lmname + "_final_" + cname + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write("uvnormal_" + lmname + (compressedGBuffer ? ".lz4" : ".dds")); + foreach(var lmgroup in groupListPlain) + { + if (lmgroup.parentName != lmname) continue; + fcomp.Write(lmgroup.name + "_final_" + cname + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write("uvnormal_" + lmgroup.name + (compressedGBuffer ? ".lz4" : ".dds")); + } + fcomp.Close(); + } + } + else + { + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + #if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; + #endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "postadd " + scenePathQuoted + " \"" + lmname + "_final_HDR" + (compressedOutput ? ".lz4" : ".dds") + + "\"" + " " + PASS_HALF + " " + 0 + " " + LMID + " comp_addbuckets" + LMID + ".bin"; + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add("Compositing lightmaps for " + lmname + "..."); + + var fcomp = new BinaryWriter(File.Open(scenePath + "/comp_addbuckets" + LMID + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("comp_addbuckets" + LMID + ".bin"); + fcomp.Write(lmname + "_final_HDR" + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write("uvnormal_" + lmname + (compressedGBuffer ? ".lz4" : ".dds")); + foreach(var lmgroup in groupListPlain) + { + //Debug.LogError("Cur: "+lmname+", "+LMID+", this parent: " + lmgroup.name+", "+lmgroup.parentID); + //if (lmgroup.parentID != LMID) continue; + if (lmgroup.parentName != lmname) continue; + fcomp.Write(lmgroup.name + "_final_HDR" + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write("uvnormal_" + lmgroup.name + (compressedGBuffer ? ".lz4" : ".dds")); + } + fcomp.Close(); + } + + if (dominantDirMode) + { + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "postdiradd " + scenePathQuoted + " \"" + lmname + "_final_Dir" + (compressedOutput ? ".lz4" : ".dds") + + "\"" + " " + PASS_DIRECTION + " " + 0 + " " + LMID + " dircomp_addbuckets" + LMID + ".bin"; + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add("Compositing directions for " + lmname + "..."); + + var fcomp = new BinaryWriter(File.Open(scenePath + "/dircomp_addbuckets" + LMID + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("dircomp_addbuckets" + LMID + ".bin"); + fcomp.Write(lmname + "_final_Dir" + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write("uvnormal_" + lmname + (compressedGBuffer ? ".lz4" : ".dds")); + foreach(var lmgroup in groupListPlain) + { + if (lmgroup.parentName != lmname) continue; + fcomp.Write(lmgroup.name + "_final_Dir" + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write("uvnormal_" + lmgroup.name + (compressedGBuffer ? ".lz4" : ".dds")); + } + fcomp.Close(); + } + + if (shadowmask) + { + var maskNames = lightmapMasks[LMID]; + var maskLights = lightmapMaskLights[LMID]; + if (maskNames != null) + { + for(int c=0; c 0 && lmgroup.parentName != "|") + { + process = false; + } + /*else if (lmgroup.parentName == "|") + { + foreach(var lmgroup2 in groupListPlain) + { + if (lmgroup2.parentName == lmgroup.name) + { + var maskNames2 = lightmapMasks[lmgroup2.id]; + var maskLights2 = lightmapMaskLights[lmgroup2.id]; + var maskDenoise2 = lightmapMaskDenoise[lmgroup2.id]; + int channels2 = maskNames2.Count; + for(int j=0; j 0 && process) + { + var fcomp = new BinaryWriter(File.Open(scenePath + ("/masks_" + LMID + ".bin"), FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("masks_" + LMID + ".bin"); + fcomp.Write(maskNames[0].Count); + fcomp.Write(maskNames.Count > 1 ? maskNames[1].Count : 0); + fcomp.Write(maskNames.Count > 2 ? maskNames[2].Count : 0); + fcomp.Write(maskNames.Count > 3 ? maskNames[3].Count : 0); + for(int channel=0; channel list; + deferredCommandsFallback[deferredCommands.Count - 1] = list = new List(); + + int denoiseRes = resolution; + while(denoiseRes > 64) + { + denoiseRes /= 2; + list.Add(firstArgs + " " + denoiseRes + " 0"); + } + } + else + { + // unsupported + } + } + } + fcomp.Close(); + + progressText = "Creating shadow masks for " + lmname + "..."; + if (!deferredMode) ProgressBarShow(progressText, (progressStepsDone / (float)progressSteps), true); + if (userCanceled) yield break; + yield return null; + + var outPath = Application.dataPath + "/" + outputPathFull + "/" + lmname + "_mask" + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga"); + if (File.Exists(outPath)) ValidateFileAttribs(outPath); + + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + startInfo.WorkingDirectory = "Assets/Editor/x64/Bakery"; + startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/combineMasks.exe"; + startInfo.CreateNoWindow = true; + if (vertexBake) + { + startInfo.Arguments = "\"" + scenePath + "/" + lmname + "_mask.lz4\" "; + } + else + { + startInfo.Arguments = "\"" + outPath + "\" "; + } + /*maskNames[0] + " "; + if (maskNames.Count > 1) startInfo.Arguments += maskNames[1] + " "; + if (maskNames.Count > 2) startInfo.Arguments += maskNames[2] + " "; + if (maskNames.Count > 3) startInfo.Arguments += maskNames[3] + " ";*/ + startInfo.Arguments += + "\"" + scenePath + ("/masks_" + LMID + ".bin") + "\" " + + "\"" + scenePath + "/\""; + + //for(int i=0; i list; + deferredCommandsFallback[deferredCommands.Count - 1] = list = new List(); + + int denoiseRes = resolution; + while(denoiseRes > 64) + { + denoiseRes /= 2; + list.Add(firstArgs + " " + denoiseRes + " 0"); + } + } + else + { + // unsupported + } + } + + if (!lightmapHasColor[LMID]) yield break; + + // Apply AO if needed + if (hackAOIntensity > 0 && hackAOSamples > 0 && !rnmMode && !shMode && !lmgroup.probes && rmode != (int)RenderMode.AmbientOcclusionOnly) + { + progressText = "Applying AO to " + lmname + "..."; + if (userCanceled) yield break;//return false; + + var fcomp = new BinaryWriter(File.Open(scenePath + "/addao_" + LMID + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("addao_" + LMID + ".bin"); + fcomp.Write(lmname + (shMode ? "_final_L0" : "_final_HDR") + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write(lmname + "_ao_Mask" + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write(hackAOIntensity); + fcomp.Close(); + + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "addao " + scenePathQuoted + " \"" + lmname + (shMode ? "_final_L0" : "_final_HDR") + (compressedOutput ? ".lz4" : ".dds") + + "\"" + " " + PASS_HALF + " " + 0 + " " + LMID + " addao_" + LMID + ".bin"; + + deferredFileSrc.Add("");//scenePath + "/addao_" + LMID + ".bin"); + deferredFileDest.Add("");//scenePath + "/addao.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + } + + // Denoise + if (denoise && !vertexBake) + { + if (!shMode && !rnmMode) + { + progressText = "Denoising " + lmname + "..."; + if (userCanceled) yield break;//return false; + yield return null; + + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + startInfo.WorkingDirectory = "Assets/Editor/x64/Bakery"; + startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/denoiser"+denoiseMod+".exe"; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "c \"" + scenePath + "/" + lmname + "_final_HDR" + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + scenePath + "/" + lmname + "_final_HDR" + (compressedOutput ? ".lz4" : ".dds") + "\""; + string firstArgs = startInfo.Arguments; + startInfo.Arguments += " " + resolution + " " + (denoise2x ? 1 : 0); + + if (deferredMode) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + List list; + deferredCommandsFallback[deferredCommands.Count - 1] = list = new List(); + + int denoiseRes = resolution; + while(denoiseRes > 64) + { + denoiseRes /= 2; + list.Add(firstArgs + " " + denoiseRes + " " + (denoise2x ? 1 : 0)); + } + } + } + } + progressStepsDone++; + + string progressText2; + + if (rnmMode && lightmapHasRNM[LMID]) + { + for(int c=0; c<3; c++) + { + // Compose RNM + progressText2 = "Composing RNM" + c + " for " + lmname + "..."; + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "add " + scenePathQuoted + " \"" + lmname + "_final_RNM" + c + (compressedOutput ? ".lz4" : ".dds") + + "\" " + PASS_HALF + " " + 0 + " " + LMID + " rnm" + c +"comp_" + LMID + ".bin"; + if (deferredMode) + { + deferredFileSrc.Add("");//scenePath + "/rnm" + c +"comp_" + LMID + ".bin"); + deferredFileDest.Add("");//scenePath + "/comp.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText2); + } + else + { + Debug.LogError("Not supported"); + } + + if (hackAOIntensity > 0 && hackAOSamples > 0) + { + progressText = "Applying AO to " + lmname + "..."; + //for(int c=0; c<3; c++) + { + var fcomp = new BinaryWriter(File.Open(scenePath + "/addao_" + LMID + "_" + c + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("addao_" + LMID + "_" + c + ".bin"); + fcomp.Write(lmname + "_final_RNM" + c + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write(lmname + "_ao_Mask" + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write(hackAOIntensity); + fcomp.Close(); + + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "addao " + scenePathQuoted + " \"" + lmname + "_final_RNM" + c + (compressedOutput ? ".lz4" : ".dds") + + "\"" + " " + PASS_HALF + " " + 0 + " " + LMID + " addao_" + LMID + "_" + c + ".bin"; + + if (deferredMode) + { + deferredFileSrc.Add("");//scenePath + "/addao_" + LMID + "_" + c + ".bin"); + deferredFileDest.Add("");//scenePath + "/addao.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + } + } + } + + if (denoise && !vertexBake) + { + progressText = "Denoising RNM" + c + " for " + lmname + "..."; + if (userCanceled) yield break; + yield return null; + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + startInfo.WorkingDirectory = "Assets/Editor/x64/Bakery"; + startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/denoiser"+denoiseMod+".exe"; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "c \"" + scenePath + "/" + lmname + "_final_RNM" + c + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + scenePath + "/" + lmname + "_final_RNM" + c + (compressedOutput ? ".lz4" : ".dds") + "\""; + string firstArgs = startInfo.Arguments; + startInfo.Arguments += " " + resolution + " " + (denoise2x ? 1 : 0); + if (deferredMode) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + List list; + deferredCommandsFallback[deferredCommands.Count - 1] = list = new List(); + + int denoiseRes = resolution; + while(denoiseRes > 64) + { + denoiseRes /= 2; + list.Add(firstArgs + " " + denoiseRes + " " + (denoise2x ? 1 : 0)); + } + } + else + { + Debug.LogError("Not supported"); + } + } + } + } + + if (shMode && lightmapHasRNM[LMID]) + { + // Compose SH + progressText2 = "Composing SH " + " for " + lmname + "..."; + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "addsh " + scenePathQuoted + " \"" + lmname + "_final_" + + "\" " + PASS_HALF + " " + 0 + " " + LMID + " shcomp_" + LMID + ".bin"; + if (deferredMode) + { + deferredFileSrc.Add("");//scenePath + "/shcomp_" + LMID + ".bin"); + deferredFileDest.Add("");//scenePath + "/shcomp.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText2); + } + else + { + Debug.LogError("Not supported"); + } + + if (hackAOIntensity > 0 && hackAOSamples > 0 && !lmgroup.probes) + { + progressText = "Applying AO to " + lmname + "..."; + var fcomp = new BinaryWriter(File.Open(scenePath + "/addao_" + LMID + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("addao_" + LMID + ".bin"); + fcomp.Write(lmname + (shMode ? "_final_L0" : "_final_HDR") + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write(lmname + "_ao_Mask" + (compressedOutput ? ".lz4" : ".dds")); + fcomp.Write(hackAOIntensity); + fcomp.Close(); + + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = startInfo.WorkingDirectory + "/" + ftraceExe; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "addao " + scenePathQuoted + " \"" + lmname + (shMode ? "_final_L0" : "_final_HDR") + (compressedOutput ? ".lz4" : ".dds") + + "\"" + " " + PASS_HALF + " " + 0 + " " + LMID + " addao_" + LMID + ".bin"; + + deferredFileSrc.Add("");//scenePath + "/addao_" + LMID + ".bin"); + deferredFileDest.Add("");//scenePath + "/addao.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + } + + if (denoise && !vertexBake) + { + progressText = "Denoising SH for " + lmname + "..."; + if (userCanceled) yield break; + yield return null; + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + startInfo.WorkingDirectory = "Assets/Editor/x64/Bakery"; + startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/denoiser"+denoiseMod+".exe"; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "s "; + startInfo.Arguments += "\"" + scenePath + "/" + lmname + "_final_L0" + (compressedOutput ? ".lz4" : ".dds") + + "\" \"" + scenePath + "/" + lmname + "_final_L1x" + (compressedOutput ? ".lz4" : ".dds") + + "\" \"" + scenePath + "/" + lmname + "_final_L1y" + (compressedOutput ? ".lz4" : ".dds") + + "\" \"" + scenePath + "/" + lmname + "_final_L1z" + (compressedOutput ? ".lz4" : ".dds") + + "\""; + string firstArgs = startInfo.Arguments; + startInfo.Arguments += " " + resolution + " 0"; + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + List list; + deferredCommandsFallback[deferredCommands.Count - 1] = list = new List(); + + int denoiseRes = resolution; + while(denoiseRes > 64) + { + denoiseRes /= 2; + list.Add(firstArgs + " " + denoiseRes + " 0"); + } + } + + + } + + // Fix seams + if (fixSeams && !vertexBake) + { + progressText = "Fixing seams " + lmname + "..."; + if (userCanceled) yield break;//return false; + yield return null; + + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + startInfo.WorkingDirectory = "Assets/Editor/x64/Bakery"; + startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/seamfixer.exe"; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "\"" + scenePath + "\" \"" + + LMID + "\" \""; + if (shMode) + { + startInfo.Arguments += lmname + "_final_L0" + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + + lmname + "_final_L1x" + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + + lmname + "_final_L1y" + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + + lmname + "_final_L1z" + (compressedOutput ? ".lz4" : ".dds") + "\""; + } + else if (rnmMode) + { + startInfo.Arguments += lmname + "_final_RNM0" + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + + lmname + "_final_RNM1" + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + + lmname + "_final_RNM2" + (compressedOutput ? ".lz4" : ".dds") + "\""; + } + else if (dominantDirMode) + { + startInfo.Arguments += lmname + "_final_HDR" + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + + lmname + "_final_Dir" + (compressedOutput ? ".lz4" : ".dds"); + } + else + { + startInfo.Arguments += lmname + "_final_HDR" + (compressedOutput ? ".lz4" : ".dds") + "\""; + } + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + } + progressStepsDone++; + } + + IEnumerator RenderLMEncode(int LMID, string lmname, int resolution, bool vertexBake, int lmgroupRenderDirMode, int lmgroupRenderMode) + { + if (vertexBake) yield break; + + System.Diagnostics.ProcessStartInfo startInfo; + + var dirMode = lmgroupRenderDirMode == (int)BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)lmgroupRenderDirMode; + var dominantDirMode = dirMode == (int)ftRenderLightmap.RenderDirMode.DominantDirection; + var rnmMode = dirMode == (int)ftRenderLightmap.RenderDirMode.RNM && lightmapHasRNM[LMID]; + var shMode = dirMode == (int)ftRenderLightmap.RenderDirMode.SH && lightmapHasRNM[LMID]; + var shModeProbe = dirMode == (int)BakeryLightmapGroup.RenderDirMode.ProbeSH && lightmapHasRNM[LMID]; + if (shModeProbe) shMode = true; + + var progressText2 = "Encoding " + lmname + "..."; + if (userCanceled) yield break;//return false; + progressStepsDone++; + yield return null; + + int maxValue = 1024; +#if UNITY_2019_1_OR_NEWER + if (GraphicsSettings.renderPipelineAsset != null) + { + var srpType = GraphicsSettings.renderPipelineAsset.GetType().ToString(); + if (srpType.Contains("HDRenderPipelineAsset")) + { + maxValue = 64000; + } + } +#endif + + if (encode)// && !vertexBake)// && File.Exists(scenePath + "/" + lmname + "_final_HDR.dds")) + { + if (vertexBake) + { + if (deferredMode) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(null); + deferredCommandDesc.Add(progressText2); + + var gr = new BakeryLightmapGroupPlain(); + gr.id = LMID; + gr.name = lmname; + deferredCommandsHalf2VB[deferredCommands.Count - 1] = gr; + } + else + { + //GenerateVertexBakedMeshes(LMID, lmname); + } + } + else// if (!bc6h) + { + if (!shMode && !rnmMode) + { + var outPath = Application.dataPath + "/" + outputPathFull + "/" + lmname + "_final.hdr"; + if (File.Exists(outPath)) ValidateFileAttribs(outPath); + + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + //startInfo.WorkingDirectory = scenePath; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/halffloat2hdr.exe"; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "\"" + scenePath + "/" + lmname + "_final_HDR" + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + outPath + "\" " + maxValue; + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText2); + if (clientMode) ftClient.serverGetFileList.Add(lmname + "_final.hdr"); + } + } + } + + // Encode directions + if (dominantDirMode && !vertexBake && lightmapHasDir[LMID]) + { + var outPath = Application.dataPath + "/" + outputPathFull + "/" + lmname + "_dir" + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga"); + if (File.Exists(outPath)) ValidateFileAttribs(outPath); + + progressText2 = "Encoding direction for " + lmname + "..."; + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + //startInfo.WorkingDirectory = scenePath; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/rgba2tga.exe"; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "\"" + scenePath + "/" + lmname + "_final_Dir" + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + outPath + "\" " + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? "p" : ""); + + if (deferredMode) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText2); + if (clientMode) ftClient.serverGetFileList.Add(lmname + "_dir" + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga")); + } + else + { + Debug.LogError("Not supported"); + } + } + + if (rnmMode && !vertexBake && lightmapHasRNM[LMID]) + { + for(int c=0; c<3; c++) + { + var outPath = Application.dataPath + "/" + outputPathFull + "/" + lmname + "_RNM" + c + ".hdr"; + if (File.Exists(outPath)) ValidateFileAttribs(outPath); + + // Encode RNM + progressText2 = "Encoding RNM" + c + " for " + lmname + "..."; + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + //startInfo.WorkingDirectory = scenePath; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/halffloat2hdr.exe"; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "\"" + scenePath + "/" + lmname + "_final_RNM" + c + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + outPath + "\" " + maxValue; + if (deferredMode) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText2); + if (clientMode) ftClient.serverGetFileList.Add(lmname + "_RNM" + c + ".hdr"); + } + else + { + Debug.LogError("Not supported"); + } + } + } + + if (shMode && !vertexBake && lightmapHasRNM[LMID]) + { + var outPath = Application.dataPath + "/" + outputPathFull + "/" + lmname + "_L0.hdr"; + if (File.Exists(outPath)) ValidateFileAttribs(outPath); + + progressText2 = "Encoding SH L0 for " + lmname + "..."; + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + //startInfo.WorkingDirectory = scenePath; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/halffloat2hdr.exe"; + startInfo.CreateNoWindow = true; + startInfo.Arguments = "\"" + scenePath + "/" + lmname + "_final_L0" + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + outPath + "\" " + maxValue; + if (deferredMode) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText2); + if (clientMode) ftClient.serverGetFileList.Add(lmname + "_L0.hdr"); + } + else + { + Debug.LogError("Not supported"); + } + + progressText2 = "Encoding SH L1 for " + lmname + "..."; + for(int i=0; i<3; i++) + { + startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + //startInfo.WorkingDirectory = scenePath; +#if !LAUNCH_VIA_DLL + startInfo.WorkingDirectory = dllPath + "/Bakery"; +#endif + startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/rgba2tga.exe"; + startInfo.CreateNoWindow = true; + string comp; + if (i==0) + { + comp = "x"; + } + else if (i==1) + { + comp = "y"; + } + else + { + comp = "z"; + } + + var outPath1 = Application.dataPath + "/" + outputPathFull + "/" + lmname + "_L1" + comp + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga"); + if (File.Exists(outPath1)) ValidateFileAttribs(outPath1); + + startInfo.Arguments = "\"" + scenePath + "/" + lmname + "_final_L1" + comp + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + outPath1 + "\" " + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? "p" : ""); + + if (deferredMode) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText2); + if (clientMode) ftClient.serverGetFileList.Add(lmname + "_L1" + comp + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga")); + } + else + { + Debug.LogError("Not supported"); + } + } + } + } + + public static System.IntPtr RunFTrace(string args, bool visible = false) + { + DebugLogInfo("Running ftrace " + args); +#if LAUNCH_VIA_DLL + System.IntPtr exeProcess; + + if (visible) + { + exeProcess = RunLocalProcessVisible(ftraceExe+" "+args); + } + else + { + exeProcess = RunLocalProcess(ftraceExe+" "+args, true); + } + + if (exeProcess == (System.IntPtr)null) + { + Debug.LogError(ftraceExe + " launch failed (see console for details)"); + return (System.IntPtr)0; + } + return exeProcess; +#else + Debug.LogError("Not supported"); + return (System.IntPtr)0; +#endif + } + + static void FindGlobalStorage() + { + if (gstorage == null) + { + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + gstorage = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftGlobalStorage.asset", typeof(ftGlobalStorage)) as ftGlobalStorage; + } + } + + public static void LoadDefaultSettings(ftLightmapsStorage storage) + { + FindGlobalStorage(); + if (gstorage == null) return; + ftLightmapsStorage.CopySettings(gstorage, storage); + } + + static List roots; + public static ftLightmapsStorage FindRenderSettingsStorage() + { + // Load saved settings + GameObject go = null; + if (roots == null) roots = new List(); + + try + { + SceneManager.GetActiveScene().GetRootGameObjects(roots); + } + catch + { + // scene is not loaded, oops + return null; + } + + go = roots.Find( g => g.name == "!ftraceLightmaps" ); + + if (go == null) go = GameObject.Find("!ftraceLightmaps"); + if (go == null) { + go = new GameObject(); + go.name = "!ftraceLightmaps"; + go.hideFlags = HideFlags.HideInHierarchy; + } + var storage = go.GetComponent(); + if (storage == null) { + storage = go.AddComponent(); + LoadDefaultSettings(storage); + } + return storage; + } + + public static void LoadStaticAtlasingSettings() + { + var storage = FindRenderSettingsStorage(); + ftRenderLightmap.tileSize = storage.renderSettingsTileSize; + ftBuildGraphics.texelsPerUnit = storage.renderSettingsTexelsPerUnit; + ftBuildGraphics.autoAtlas = storage.renderSettingsAutoAtlas; + ftBuildGraphics.unwrapUVs = storage.renderSettingsUnwrapUVs; + ftBuildGraphics.forceDisableUnwrapUVs = storage.renderSettingsForceDisableUnwrapUVs; + ftBuildGraphics.maxAutoResolution = storage.renderSettingsMaxAutoResolution; + ftBuildGraphics.minAutoResolution = storage.renderSettingsMinAutoResolution; + ftRenderLightmap.checkOverlaps = storage.renderSettingsCheckOverlaps; + ftBuildGraphics.texelsPerUnitPerMap = storage.renderSettingsTexelsPerMap; + ftBuildGraphics.mainLightmapScale = storage.renderSettingsTexelsColor; + ftBuildGraphics.maskLightmapScale = storage.renderSettingsTexelsMask; + ftBuildGraphics.dirLightmapScale = storage.renderSettingsTexelsDir; + ftBuildGraphics.splitByScene = storage.renderSettingsSplitByScene; + ftBuildGraphics.uvPaddingMax = storage.renderSettingsUVPaddingMax; + ftBuildGraphics.postPacking = storage.renderSettingsPostPacking; + ftBuildGraphics.holeFilling = storage.renderSettingsHoleFilling; + ftBuildGraphics.atlasPacker = storage.renderSettingsAtlasPacker; + } + + public void LoadRenderSettings() + { + FindGlobalStorage(); + if (gstorage != null) + { + foundCompatibleSetup = gstorage.foundCompatibleSetup; + scenePath = gstorage.renderSettingsTempPath; + } + + instance = this; + var storage = instance.renderSettingsStorage = FindRenderSettingsStorage(); + if (storage == null) return; + bounces = storage.renderSettingsBounces; + instance.giSamples = storage.renderSettingsGISamples; + giBackFaceWeight = storage.renderSettingsGIBackFaceWeight; + ftRenderLightmap.tileSize = storage.renderSettingsTileSize; + instance.priority = storage.renderSettingsPriority; + instance.texelsPerUnit = storage.renderSettingsTexelsPerUnit; + ftRenderLightmap.forceRefresh = storage.renderSettingsForceRefresh; + instance.forceRebuildGeometry = storage.renderSettingsForceRebuildGeometry; + instance.performRendering = storage.renderSettingsPerformRendering; + instance.userRenderMode = (RenderMode)storage.renderSettingsUserRenderMode; + instance.settingsMode = (SettingsMode)storage.renderSettingsSettingsMode; + instance.fixSeams = storage.renderSettingsFixSeams; + instance.denoise = storage.renderSettingsDenoise; + instance.denoise2x = storage.renderSettingsDenoise2x; + instance.encode = storage.renderSettingsEncode; + instance.encodeMode = storage.renderSettingsEncodeMode; + ftBuildGraphics.overwriteWarning = storage.renderSettingsOverwriteWarning; + ftBuildGraphics.autoAtlas = storage.renderSettingsAutoAtlas; + ftBuildGraphics.unwrapUVs = storage.renderSettingsUnwrapUVs; + ftBuildGraphics.forceDisableUnwrapUVs = storage.renderSettingsForceDisableUnwrapUVs; + ftBuildGraphics.maxAutoResolution = storage.renderSettingsMaxAutoResolution; + ftBuildGraphics.minAutoResolution = storage.renderSettingsMinAutoResolution; + instance.unloadScenesInDeferredMode = storage.renderSettingsUnloadScenes; + ftRenderLightmap.adjustSamples = storage.renderSettingsAdjustSamples; + ftRenderLightmap.giLodMode = (GILODMode)storage.renderSettingsGILODMode; + ftRenderLightmap.giLodModeEnabled = storage.renderSettingsGILODModeEnabled; + ftRenderLightmap.checkOverlaps = storage.renderSettingsCheckOverlaps; + ftRenderLightmap.outputPath = storage.renderSettingsOutPath == "" ? "BakeryLightmaps" : storage.renderSettingsOutPath; + ftRenderLightmap.useScenePath = storage.renderSettingsUseScenePath; + hackEmissiveBoost = storage.renderSettingsHackEmissiveBoost; + hackIndirectBoost = storage.renderSettingsHackIndirectBoost; + hackAOIntensity = renderSettingsStorage.renderSettingsHackAOIntensity; + hackAORadius = renderSettingsStorage.renderSettingsHackAORadius; + hackAOSamples = renderSettingsStorage.renderSettingsHackAOSamples; + showAOSettings = renderSettingsStorage.renderSettingsShowAOSettings; + showTasks = renderSettingsStorage.renderSettingsShowTasks; + showTasks2 = renderSettingsStorage.renderSettingsShowTasks2; + showPaths = renderSettingsStorage.renderSettingsShowPaths; + showNet = renderSettingsStorage.renderSettingsShowNet; + showPerf = renderSettingsStorage.renderSettingsShowPerf; + //showCompression = renderSettingsStorage.renderSettingsShowCompression; + ftBuildGraphics.texelsPerUnitPerMap = renderSettingsStorage.renderSettingsTexelsPerMap; + ftBuildGraphics.mainLightmapScale = renderSettingsStorage.renderSettingsTexelsColor; + ftBuildGraphics.maskLightmapScale = renderSettingsStorage.renderSettingsTexelsMask; + ftBuildGraphics.dirLightmapScale = renderSettingsStorage.renderSettingsTexelsDir; + useUnityForOcclsusionProbes = renderSettingsStorage.renderSettingsOcclusionProbes; + lastBakeTime = renderSettingsStorage.lastBakeTime; + beepOnFinish = renderSettingsStorage.renderSettingsBeepOnFinish; + ftBuildGraphics.exportTerrainAsHeightmap = renderSettingsStorage.renderSettingsExportTerrainAsHeightmap; + ftBuildGraphics.exportTerrainTrees = renderSettingsStorage.renderSettingsExportTerrainTrees; + rtxMode = renderSettingsStorage.renderSettingsRTXMode; + lightProbeMode = (LightProbeMode)renderSettingsStorage.renderSettingsLightProbeMode; + clientMode = renderSettingsStorage.renderSettingsClientMode; + ftClient.serverAddress = renderSettingsStorage.renderSettingsServerAddress; + unwrapper = (ftGlobalStorage.Unwrapper)renderSettingsStorage.renderSettingsUnwrapper; + denoiserType = (ftGlobalStorage.DenoiserType)renderSettingsStorage.renderSettingsDenoiserType; + //legacyDenoiser = renderSettingsStorage.renderSettingsLegacyDenoiser; + ftBuildGraphics.atlasPacker = renderSettingsStorage.renderSettingsAtlasPacker; + sampleDivisor = storage.renderSettingsSampleDiv; + if (storage.renderSettingsSector != null) curSector = (BakerySector)storage.renderSettingsSector; + + ftraceExe = rtxMode ? ftraceExe6 : ftraceExe1; + //scenePath = storage.renderSettingsTempPath; + + if (scenePath == "") scenePath = System.Environment.GetEnvironmentVariable("TEMP", System.EnvironmentVariableTarget.Process) + "\\frender"; + ftBuildGraphics.scenePath = scenePath; + scenePathQuoted = "\"" + scenePath + "\""; + +#if UNITY_2017_1_OR_NEWER + isDistanceShadowmask = QualitySettings.shadowmaskMode == ShadowmaskMode.DistanceShadowmask; +#else + isDistanceShadowmask = storage.renderSettingsDistanceShadowmask; +#endif + showDirWarning = storage.renderSettingsShowDirWarning; + renderDirMode = (RenderDirMode)storage.renderSettingsRenderDirMode; + showCheckerSettings = storage.renderSettingsShowCheckerSettings; + usesRealtimeGI = storage.usesRealtimeGI; + samplesWarning = storage.renderSettingsSamplesWarning; + prefabWarning = storage.renderSettingsPrefabWarning; + ftBuildGraphics.splitByScene = storage.renderSettingsSplitByScene; + ftBuildGraphics.uvPaddingMax = storage.renderSettingsUVPaddingMax; + ftBuildGraphics.postPacking = storage.renderSettingsPostPacking; + ftBuildGraphics.holeFilling = storage.renderSettingsHoleFilling; + compressVolumes = storage.renderSettingsCompressVolumes; + } + + void OnEnable() + { + LoadRenderSettings(); + } + + [MenuItem ("Bakery/Render lightmap...", false, 0)] + public static void RenderLightmap () + { + instance = (ftRenderLightmap)GetWindow(typeof(ftRenderLightmap)); + instance.titleContent.text = "Bakery"; + var edPath = ftLightmaps.GetEditorPath(); + var icon = EditorGUIUtility.Load(edPath + "icon.png") as Texture2D; + instance.titleContent.image = icon; + instance.Show(); + ftLightmaps.GetRuntimePath(); + } +} + +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRenderLightmap.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRenderLightmap.cs.meta new file mode 100644 index 00000000..f7587a31 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRenderLightmap.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dc64e635488f60747bf5e9025c593285 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRestorePaddingMenu.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRestorePaddingMenu.cs new file mode 100644 index 00000000..0ea8020b --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRestorePaddingMenu.cs @@ -0,0 +1,88 @@ +using UnityEngine; +using UnityEditor; +using UnityEngine.SceneManagement; + +public class ftRestorePaddingMenu +{ + [MenuItem("Bakery/Utilities/Re-adjust UV padding", false, 43)] + private static void RestorePadding() + { + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + var gstorage = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftGlobalStorage.asset", typeof(ftGlobalStorage)) as ftGlobalStorage; + + if (gstorage == null) + { + Debug.Log("Bakery is not initalized"); + return; + } + + if (EditorUtility.DisplayDialog("Bakery", "Re-unwrap and reimport lightmapped scene models to match last bake?", "OK", "Cancel")) + { + var sceneCount = SceneManager.sceneCount; + int reimported = 0; + for(int i=0; i(); + if (store == null) continue; + + for(int j=0; j= 0) + { + if (dataExisting.padding[ind] != data.padding[k]) + { + dataExisting.padding[ind] = data.padding[k]; + updated = true; + } + if (dataExisting.unwrapper[ind] != data.unwrapper[k]) + { + dataExisting.unwrapper[ind] = data.unwrapper[k]; + updated = true; + } + } + else + { + dataExisting.meshName.Add( data.meshName[k] ); + dataExisting.padding.Add( data.padding[k] ); + dataExisting.unwrapper.Add( data.unwrapper[k] ); + updated = true; + } + } + } + if (updated) + { +#if UNITY_2017_1_OR_NEWER + gstorage.SyncModifiedAsset(mstoreIndex); +#endif + EditorUtility.SetDirty(gstorage); + (AssetImporter.GetAtPath(path) as ModelImporter).SaveAndReimport(); + reimported++; + } + } + } + Debug.Log(reimported > 0 ? ("Updated " + reimported + " models") : "No changes detected"); + } + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRestorePaddingMenu.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRestorePaddingMenu.cs.meta new file mode 100644 index 00000000..cec7770a --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftRestorePaddingMenu.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1acbda60094b1b14fa803d9ce4fb88d3 +timeCreated: 1557694522 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavePadding.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavePadding.cs new file mode 100644 index 00000000..ea4f56a6 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavePadding.cs @@ -0,0 +1,55 @@ +#if UNITY_EDITOR + +using UnityEngine; +using UnityEditor; +using UnityEngine.SceneManagement; +using System.IO; +using System.Collections.Generic; + +public class ftSavePaddingMenu +{ + [MenuItem("Bakery/Utilities/Save UV padding to asset", false, 60)] + private static void RestorePadding() + { + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + var gstorage = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftGlobalStorage.asset", typeof(ftGlobalStorage)) as ftGlobalStorage; + + if (gstorage == null) + { + Debug.Log("Bakery is not initalized"); + return; + } + + var sel = Selection.objects; + var pathList = new List(); + + for(int i=0; i(); + asset.data = mod; + AssetDatabase.CreateAsset(asset, Path.GetDirectoryName(pathList[i]) + "/" + Path.GetFileNameWithoutExtension(pathList[i]) + "_padding.asset"); + Debug.Log("Created padding asset for " + pathList[i]); + ctr++; + } + + AssetDatabase.SaveAssets(); + Debug.Log("Created " + ctr + " UV padding assets"); + } +} + +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavePadding.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavePadding.cs.meta new file mode 100644 index 00000000..77fac003 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavePadding.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1b9bbae7393eaa04db704d80e254be86 +timeCreated: 1565341770 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSaveSettingsMenu.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSaveSettingsMenu.cs new file mode 100644 index 00000000..ba26d3cd --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSaveSettingsMenu.cs @@ -0,0 +1,53 @@ +using UnityEngine; +using UnityEditor; +using UnityEngine.SceneManagement; + +public class ftSaveSettingsMenu +{ + [MenuItem("Bakery/Utilities/Save settings as default", false, 41)] + private static void SaveSettings() + { + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + var gstorage = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftGlobalStorage.asset", typeof(ftGlobalStorage)) as ftGlobalStorage; + + if (gstorage == null) + { + Debug.Log("Bakery is not initalized"); + return; + } + + if (EditorUtility.DisplayDialog("Bakery", "Save current scene settings as global defaults?", "OK", "Cancel")) + { + var storage = ftRenderLightmap.FindRenderSettingsStorage(); + ftRenderLightmap bakery = ftRenderLightmap.instance != null ? ftRenderLightmap.instance : new ftRenderLightmap(); + bakery.LoadRenderSettings(); + ftLightmapsStorage.CopySettings(storage, gstorage); + EditorUtility.SetDirty(gstorage); + Debug.Log("Default settings saved"); + } + } + + [MenuItem("Bakery/Utilities/Load default settings", false, 42)] + private static void LoadSettings() + { + var bakeryRuntimePath = ftLightmaps.GetRuntimePath(); + var gstorage = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftGlobalStorage.asset", typeof(ftGlobalStorage)) as ftGlobalStorage; + + if (gstorage == null) + { + Debug.Log("Bakery is not initalized"); + return; + } + + if (EditorUtility.DisplayDialog("Bakery", "Set default baking settings for the current scene?", "OK", "Cancel")) + { + var storage = ftRenderLightmap.FindRenderSettingsStorage(); + ftRenderLightmap bakery = ftRenderLightmap.instance != null ? ftRenderLightmap.instance : new ftRenderLightmap(); + ftLightmapsStorage.CopySettings(gstorage, storage); + EditorUtility.SetDirty(storage); + bakery.LoadRenderSettings(); + Debug.Log("Default settings loaded"); + } + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSaveSettingsMenu.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSaveSettingsMenu.cs.meta new file mode 100644 index 00000000..0c06bbe4 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSaveSettingsMenu.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6977f7d9b2482ea4cbd5535e0046efab +timeCreated: 1558111532 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavedPadding2.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavedPadding2.cs new file mode 100644 index 00000000..73d1473c --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavedPadding2.cs @@ -0,0 +1,8 @@ +using UnityEngine; +using UnityEditor; + +public class ftSavedPadding2 : ScriptableObject +{ + [SerializeField] + public ftGlobalStorage.AdjustedMesh data; +} diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavedPadding2.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavedPadding2.cs.meta new file mode 100644 index 00000000..eab1f729 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSavedPadding2.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f1b283dcb6cb8fb4e984405825d17555 +timeCreated: 1583479458 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSceneView.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSceneView.cs new file mode 100644 index 00000000..e9c278cd --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSceneView.cs @@ -0,0 +1,171 @@ +#if UNITY_EDITOR +//#if UNITY_2018_2_OR_NEWER + +using UnityEngine; +using UnityEditor; +using System; +using System.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Linq; + +/* +public class ftSceneView +{ + public static void Init() + { + var mode = SceneView.AddCameraMode("Bakery lightmap checker", "Bakery"); + } +} +*/ + +public class ftSceneView +{ + static Shader checkerShader; + public static bool enabled; + static List tempTextures; + + static void Atlas() + { + var fgo = GameObject.Find("!ftraceLightmaps"); + if (fgo == null) { + fgo = new GameObject(); + fgo.name = "!ftraceLightmaps"; + fgo.hideFlags = HideFlags.HideInHierarchy; + } + var store = fgo.GetComponent(); + if (store == null) { + store = fgo.AddComponent(); + } + ftRenderLightmap.LoadStaticAtlasingSettings(); + + Debug.Log("Atlasing..."); + ftBuildGraphics.modifyLightmapStorage = false; + ftBuildGraphics.validateLightmapStorageImmutability = false; + var exportSceneFunc = ftBuildGraphics.ExportScene(null, false, true); + while(exportSceneFunc.MoveNext()) + { + //progressBarText = ftBuildGraphics.progressBarText; + //progressBarPercent = ftBuildGraphics.progressBarPercent; + /*if (ftBuildGraphics.userCanceled) + { + ProgressBarEnd(); + DestroyImmediate(go); + foreach(var d in dynamicObjects) d.enabled = true; + yield break; + }*/ + //yield return null; + } + Debug.Log("Atlasing done"); + //ftRenderLightmap.simpleProgressBarEnd(); + ftBuildGraphics.ProgressBarEnd(true); + } + + static void ApplyNewProperties() + { + var objs = ftBuildGraphics.atlasOnlyObj; + if (objs == null) return; + var scaleOffset = ftBuildGraphics.atlasOnlyScaleOffset; + var size = ftBuildGraphics.atlasOnlySize; + var ids = ftBuildGraphics.atlasOnlyID; + var existingLmaps = LightmapSettings.lightmaps.ToList(); + tempTextures = new List(); + int maxLM = 0; + for(int i=0; i= existingLmaps.Count || + existingLmaps[objs[i].lightmapIndex] == null || + existingLmaps[objs[i].lightmapIndex].lightmapColor == null || existingLmaps[objs[i].lightmapIndex].lightmapColor.width != size[i]) + { + int s = 1;//Math.Max(size[i],1); + var tex = new Texture2D(s, s); + tempTextures.Add(tex); + tex.SetPixels32(new Color32[s*s]); + tex.Apply(); + var ldata = new LightmapData(); + ldata.lightmapColor = tex; + existingLmaps.Add(ldata); + objs[i].lightmapIndex = existingLmaps.Count - 1; + } + + var prop = new MaterialPropertyBlock(); + objs[i].GetPropertyBlock(prop); + prop.SetFloat("bakeryLightmapSize", size[i]); + int lmid = ids[i]; + if (lmid < 1000) + { + if (lmid > maxLM) maxLM = lmid; + } + UnityEngine.Random.InitState(lmid); + prop.SetVector("bakeryLightmapID", UnityEngine.Random.ColorHSV(0, 1, 0.3f, 0.3f, 1, 1)); + objs[i].SetPropertyBlock(prop); + } + + Debug.Log("Lightmap count with current settings: " + (maxLM+1)); + + LightmapSettings.lightmaps = existingLmaps.ToArray(); + } + + //[MenuItem("Bakery/Checker/Toggle")] + public static void ToggleChecker() + { + var sceneView = SceneView.lastActiveSceneView; + if (sceneView == null) + { + Debug.LogError("Can't get SceneView"); + return; + } + if (enabled) + { + tempTextures = null; + //var sceneCameras = SceneView.GetAllSceneCameras(); + //for(int i=0; i(); + asset.write = true; + + ftRenderLightmap.fullSectorRender = true; + ftBuildGraphics.modifyLightmapStorage = false; + ftBuildGraphics.validateLightmapStorageImmutability = false; + var exportSceneFunc = ftBuildGraphics.ExportScene(null, false, true, asset); + var prevSector = storage.renderSettingsSector as BakerySector; + storage.renderSettingsSector = ftRenderLightmap.curSector = vol; + while(exportSceneFunc.MoveNext()) + { + } + storage.renderSettingsSector = ftRenderLightmap.curSector = prevSector; + + if (asset.meshes != null && asset.meshes.Count > 0) + { + string fname; + var activeScene = SceneManager.GetActiveScene(); + if (activeScene.path.Length > 0) + { + fname = Path.GetDirectoryName(activeScene.path) + "/" + assetName; + } + else + { + fname = "Assets/" + assetName; + } + + var tform = (target as BakerySector).transform; + asset.sectorPos = tform.position; + asset.sectorRot = tform.rotation; + + var apath = fname + ".asset"; + AssetDatabase.CreateAsset(asset, apath); + + for(int i=0; i(); + asset.write = true; + } + + ftRenderLightmap.showProgressBar = false; + ftRenderLightmap.fullSectorRender = true; + ftBuildGraphics.modifyLightmapStorage = false; + ftBuildGraphics.validateLightmapStorageImmutability = false; + var exportSceneFunc = ftBuildGraphics.ExportScene(null, false, true, asset); + var prevSector = storage.renderSettingsSector as BakerySector; + storage.renderSettingsSector = ftRenderLightmap.curSector = vol; + while(exportSceneFunc.MoveNext()) + { + } + storage.renderSettingsSector = ftRenderLightmap.curSector = prevSector; + ftRenderLightmap.showProgressBar = true; + + var outRend = asset.outsideRenderers; + vol.previewDisabledRenderers = outRend; + if (outRend != null) + { + for(int i=0; i 0) + { + GUILayout.Label("Edit capture points:"); + } + + for(int i=0; i= 0 || curSelectedC >= 0) Tools.current = Tool.None; + + for(int i=0; i= 0) + { + Handles.matrix = origHMatrix; + int i = curSelectedB; + Handles.zTest = UnityEngine.Rendering.CompareFunction.Always; + var pos = vol.tforms[i].position; + var rot = vol.tforms[i].rotation; + var scl = vol.tforms[i].localScale; + + if (!vol.previewEnabled) + { + EditorGUI.BeginChangeCheck(); + if (lastTool == Tool.Move) + { + pos = Handles.PositionHandle(pos, Quaternion.identity); + } + else if (lastTool == Tool.Rotate) + { + rot = Handles.RotationHandle(rot, pos); + } + else if (lastTool == Tool.Scale) + { + scl = Handles.ScaleHandle(scl, pos, rot, HandleUtility.GetHandleSize(pos)); + } + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(vol.tforms[i], "Change Bounds"); + vol.tforms[i].position = pos; + vol.tforms[i].rotation = rot; + vol.tforms[i].localScale = scl; + } + } + } + + Handles.matrix = Matrix4x4.identity; + Handles.color = Color.green; + + for(int i=0; i= 0) + { + int i = curSelectedC; + Handles.zTest = UnityEngine.Rendering.CompareFunction.Always; + + if (vol.cpoints[i] != null) + { + var pos = vol.cpoints[i].position; + + if (!vol.previewEnabled) + { + EditorGUI.BeginChangeCheck(); + if (lastTool == Tool.Move) + { + pos = Handles.PositionHandle(pos, Quaternion.identity); + } + + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(vol.cpoints[i], "Change capture point"); + vol.cpoints[i].position = pos; + } + } + } + } + } +} +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSectorInspector.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSectorInspector.cs.meta new file mode 100644 index 00000000..7a75de1f --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSectorInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: adbe9477f3f37ce4b9269e796a502ed0 +timeCreated: 1619369355 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSettingsProvider.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSettingsProvider.cs new file mode 100644 index 00000000..3cdf1250 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSettingsProvider.cs @@ -0,0 +1,69 @@ +using UnityEditor; +using UnityEngine; + +public class ftSettingsProvider +{ + static BakeryProjectSettings pstorage; + + static void GUIHandler(string searchContext) + { + if (pstorage == null) pstorage = ftLightmaps.GetProjectSettings(); + if (pstorage == null) return; + + var so = new SerializedObject(pstorage); + + var prev = EditorGUIUtility.labelWidth; + EditorGUIUtility.labelWidth = 280; + + EditorGUILayout.PropertyField(so.FindProperty("mipmapLightmaps"), new GUIContent("Mipmap Lightmaps", "Enable mipmapping on lightmap assets. Can cause leaks across UV charts as atlases get smaller.")); + EditorGUILayout.PropertyField(so.FindProperty("format8bit"), new GUIContent("Mask/Direction format", "")); + EditorGUILayout.PropertyField(so.FindProperty("texelPaddingForDefaultAtlasPacker"), new GUIContent("Texel padding (Default atlas packer)", "How many empty texels to add between objects' UV layouts in lightmap atlases."), GUILayout.ExpandWidth(true)); + EditorGUILayout.PropertyField(so.FindProperty("texelPaddingForXatlasAtlasPacker"), new GUIContent("Texel padding (xatlas packer)", "How many empty texels to add between objects' UV layouts in lightmap atlases.")); + EditorGUILayout.PropertyField(so.FindProperty("alphaMetaPassResolutionMultiplier"), new GUIContent("Alpha Meta Pass resolution multiplier", "Scales resolution for alpha Meta Pass maps.")); + //EditorGUILayout.PropertyField(so.FindProperty("volumeRenderMode"), new GUIContent("Volume render mode", "Render mode for volumes.")); + + var volMode = (BakeryLightmapGroup.RenderMode)so.FindProperty("volumeRenderMode").intValue; + var newVolMode = (BakeryLightmapGroup.RenderMode)EditorGUILayout.EnumPopup(new GUIContent("Volume render mode", "Render mode for volumes."), volMode); + if (volMode != newVolMode) so.FindProperty("volumeRenderMode").intValue = (int)newVolMode; + + EditorGUILayout.PropertyField(so.FindProperty("deletePreviousLightmapsBeforeBake"), new GUIContent("Delete previous lightmaps before bake", "Should previously rendered Bakery lightmaps be deleted before the new bake?")); + EditorGUILayout.PropertyField(so.FindProperty("logLevel"), new GUIContent("Log level", "Print information about the bake process to console? 0 = don't. 1 = info only; 2 = warnings only; 3 = everything.")); + EditorGUILayout.PropertyField(so.FindProperty("alternativeScaleInLightmap"), new GUIContent("Alternative Scale in Lightmap", "Make 'Scale in Lightmap' renderer property act more similar to built-in Unity behaviour.")); + EditorGUILayout.PropertyField(so.FindProperty("generateSmoothPos"), new GUIContent("Generate smooth positions", "Should we adjust sample positions to prevent incorrect shadowing on very low-poly meshes with smooth normals?")); + + EditorGUIUtility.labelWidth = prev; + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + if (GUILayout.Button("Revert to defaults")) + { + if (EditorUtility.DisplayDialog("Bakery", "Revert Bskery project settings to default?", "Yes", "No")) + { + so.FindProperty("mipmapLightmaps").boolValue = false; + so.FindProperty("format8bit").intValue = 0; + so.FindProperty("texelPaddingForDefaultAtlasPacker").intValue = 3; + so.FindProperty("texelPaddingForXatlasAtlasPacker").intValue = 1; + so.FindProperty("alphaMetaPassResolutionMultiplier").intValue = 2; + so.FindProperty("volumeRenderMode").intValue = 1000; + so.FindProperty("deletePreviousLightmapsBeforeBake").boolValue = false; + so.FindProperty("logLevel").intValue = 3; + so.FindProperty("alternativeScaleInLightmap").boolValue = false; + so.FindProperty("generateSmoothPos").boolValue = true; + } + } + + so.ApplyModifiedPropertiesWithoutUndo(); + } + +#if UNITY_2018_3_OR_NEWER + [SettingsProvider] + public static SettingsProvider CreateSettingsProvider() + { + var provider = new SettingsProvider("Project/BakeryGlobalSettings", SettingsScope.Project); + provider.label = "Bakery GPU Lightmapper"; + provider.guiHandler = GUIHandler; + return provider; + } +#endif +} diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSettingsProvider.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSettingsProvider.cs.meta new file mode 100644 index 00000000..8007a641 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSettingsProvider.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a23d6b5064fb9a9408669cb173b201a8 +timeCreated: 1622052091 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftShaderTweaks.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftShaderTweaks.cs new file mode 100644 index 00000000..c21e5cd2 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftShaderTweaks.cs @@ -0,0 +1,777 @@ +#if UNITY_EDITOR + +using UnityEngine; +using UnityEngine.Rendering; +using UnityEditor; +using System.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEditor.SceneManagement; +using UnityEngine.SceneManagement; +using System.Diagnostics; +using System.Linq; + +public class ftShaderTweaks : ScriptableWizard +{ + public bool bicubic; + public bool bicubicShadow; + public bool shadowBlend; + public bool falloff; + public bool falloffDeferred; + bool initialized = false; + //bool agree = false; + string includeGIPath; + string includeShadowPath; + string includeLightPath; + string includeDeferredPath; + string shadersDir; + + string ftSignatureBegin = "//"; + string ftSignatureBicubic = "//"; + string ftSignatureShadowmask = "//"; + string ftSignatureEnd = "//"; + string unityLightmapReadCode = "half3 bakedColor = DecodeLightmap(bakedColorTex);"; + //string unityLightMatrixDecl = "unityShadowCoord4x4 unity_WorldToLight;"; + string unityDefineLightAtten = "#define UNITY_LIGHT_ATTENUATION(destName, input, worldPos) "; + string unityGetShadowCoord = "unityShadowCoord3 lightCoord = mul(unity_WorldToLight, unityShadowCoord4(worldPos, 1)).xyz;"; + string unityGetShadowCoord4 = "unityShadowCoord4 lightCoord = mul(unity_WorldToLight, unityShadowCoord4(worldPos, 1));"; + string unityGetShadow = "fixed shadow = UNITY_SHADOW_ATTENUATION(input, worldPos);"; + string ftLightFalloff = "fixed destName = ftLightFalloff(unity_WorldToLight, worldPos)"; + //string unityLightFalloffNew = "UnitySpotAttenuate(lightCoord.xyz)"; + //string ftLightFalloffNew = "ftLightFalloff(unity_WorldToLight, worldPos)"; + //string unityLightFalloffNew2 = "UnitySpotAttenuate(worldPos)"; + //string ftLightFalloffNew2 = "ftLightFalloff(unity_WorldToLight, worldPos)"; + string unitySpotFalloffDeferred = "atten *= tex2D (_LightTextureB0,"; + string ftSpotFalloffDeferred = "atten *= ftLightFalloff(_LightPos, wpos);"; + string unityPointFalloffDeferred = "float atten = tex2D (_LightTextureB0, "; + string ftPointFalloffDeferred = "float atten = ftLightFalloff(_LightPos, wpos);"; + string unityShadowMaskRead = "UNITY_SAMPLE_TEX2D(unity_ShadowMask"; + string ftShadowMaskRead = "ftBicubicSampleShadow(unity_ShadowMask"; + string unityShadowMaskRead2 = "UNITY_SAMPLE_TEX2D_SAMPLER(unity_ShadowMask"; + string ftShadowMaskRead2 = "ftBicubicSampleShadow2(unity_ShadowMask"; + string unityShadowMaskBlend = "min(realtimeShadowAttenuation, bakedShadowAttenuation)"; + string ftShadowMaskBlend = "(realtimeShadowAttenuation * bakedShadowAttenuation)"; + + //string ftLightFalloffDeferred = "#define LIGHT_ATTENUATION ftLightFalloff(unity_WorldToLight, worldPos) * SHADOW_ATTENUATION(a))"; + + void OnInspectorUpdate() + { + Repaint(); + } + + void CopyInclude(string shadersDir) + { + var edPath = ftLightmaps.GetEditorPath(); + File.Copy(edPath + "shaderSrc/ftrace.cginc", shadersDir + "/ftrace.cginc", true); + } + + bool RevertFile(string fname) + { + var reader = new StreamReader(fname); + if (reader == null) + { + UnityEngine.Debug.LogError("Can't open " + fname); + return false; + } + var lines = new List(); + bool inBlock = false; + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line.StartsWith(ftSignatureBegin)) + { + inBlock = true; + } + else if (line.StartsWith(ftSignatureEnd)) + { + inBlock = false; + } + else if (!inBlock) + { + lines.Add(line); + } + } + reader.Close(); + + var writer = new StreamWriter(fname, false); + if (writer == null) + { + UnityEngine.Debug.LogError("Can't open " + fname); + return false; + } + for(int i=0; i(); + lines.Add(ftSignatureBegin); + lines.Add(ftSignatureBicubic); + lines.Add("#define USEFTRACE\n"); + lines.Add("#ifdef USEFTRACE"); + lines.Add("#include \"ftrace.cginc\""); + lines.Add("#endif"); + lines.Add(ftSignatureEnd); + + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line.StartsWith(ftSignatureBicubic)) + { + UnityEngine.Debug.Log("Already patched"); + patched = true; + break; + } + else if (line.Trim() == unityLightmapReadCode) + { + lines.Add(ftSignatureBegin); + lines.Add("#ifdef USEFTRACE"); + lines.Add(" half3 bakedColor = ftLightmapBicubic(data.lightmapUV.xy);"); + lines.Add("#else"); + lines.Add(ftSignatureEnd); + + lines.Add(unityLightmapReadCode); + + lines.Add(ftSignatureBegin); + lines.Add("#endif"); + lines.Add(ftSignatureEnd); + } + else + { + lines.Add(line); + } + } + reader.Close(); + + if (!patched) + { + if (!File.Exists(includeGIPath + "_backup")) File.Copy(includeGIPath, includeGIPath + "_backup"); + var writer = new StreamWriter(includeGIPath, false); + + if (writer == null) + { + UnityEngine.Debug.LogError("Can't open " + includeGIPath); + bicubic = false; + return; + } + + for(int i=0; i(); + lines.Add(ftSignatureBegin); + lines.Add(ftSignatureBicubic); + lines.Add("#define USEFTRACE\n"); + lines.Add("#ifdef USEFTRACE"); + lines.Add("#include \"ftrace.cginc\""); + lines.Add("#endif"); + lines.Add(ftSignatureEnd); + + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line.StartsWith(ftSignatureBicubic)) + { + UnityEngine.Debug.Log("Already patched"); + patched = true; + break; + } + else if (line.IndexOf(unityShadowMaskRead) >= 0) + { + lines.Add(ftSignatureBegin); + lines.Add("#ifdef USEFTRACE"); + lines.Add(line.Replace(unityShadowMaskRead, ftShadowMaskRead)); + lines.Add("#else"); + lines.Add(ftSignatureEnd); + + lines.Add(line); + + lines.Add(ftSignatureBegin); + lines.Add("#endif"); + lines.Add(ftSignatureEnd); + } + else if (line.IndexOf(unityShadowMaskRead2) >= 0) + { + lines.Add(ftSignatureBegin); + lines.Add("#ifdef USEFTRACE"); + lines.Add(line.Replace(unityShadowMaskRead2, ftShadowMaskRead2)); + lines.Add("#else"); + lines.Add(ftSignatureEnd); + + lines.Add(line); + + lines.Add(ftSignatureBegin); + lines.Add("#endif"); + lines.Add(ftSignatureEnd); + } + else + { + lines.Add(line); + } + } + reader.Close(); + + if (!patched) + { + if (!File.Exists(includeShadowPath + "_backup")) File.Copy(includeShadowPath, includeShadowPath + "_backup"); + var writer = new StreamWriter(includeShadowPath, false); + + if (writer == null) + { + UnityEngine.Debug.LogError("Can't open " + includeShadowPath); + bicubicShadow = false; + return; + } + + for(int i=0; i(); + lines.Add(ftSignatureBegin); + lines.Add(ftSignatureShadowmask); + lines.Add("#define USEFTRACE\n"); + lines.Add("#ifdef USEFTRACE"); + lines.Add("#include \"ftrace.cginc\""); + lines.Add("#endif"); + lines.Add(ftSignatureEnd); + + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line.StartsWith(ftSignatureShadowmask)) + { + UnityEngine.Debug.Log("Already patched"); + patched = true; + break; + } + else if (line.IndexOf(unityShadowMaskBlend) >= 0) + { + lines.Add(ftSignatureBegin); + lines.Add("#ifdef USEFTRACE"); + lines.Add(line.Replace(unityShadowMaskBlend, ftShadowMaskBlend)); + lines.Add("#else"); + lines.Add(ftSignatureEnd); + + lines.Add(line); + + lines.Add(ftSignatureBegin); + lines.Add("#endif"); + lines.Add(ftSignatureEnd); + } + else + { + lines.Add(line); + } + } + reader.Close(); + + if (!patched) + { + if (!File.Exists(includeShadowPath + "_backup")) File.Copy(includeShadowPath, includeShadowPath + "_backup"); + var writer = new StreamWriter(includeShadowPath, false); + + if (writer == null) + { + UnityEngine.Debug.LogError("Can't open " + includeShadowPath); + shadowBlend = false; + return; + } + + for(int i=0; i(); + lines.Add(ftSignatureBegin); + lines.Add("#define USEFTRACE\n"); + lines.Add("#ifdef USEFTRACE"); + lines.Add("#include \"ftrace.cginc\""); + lines.Add("#endif"); + lines.Add(ftSignatureEnd); + int lastIfdef = 0; + int lastEndif = 0; + int lastDefine = 0; + + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + + //if (line.IndexOf(unityLightFalloffNew) >= 0) + //{ + // lines.Add(ftSignatureBegin); + // lines.Add("/*"); + // lines.Add(ftSignatureEnd); +// + // lines.Add(line); +// + // lines.Add(ftSignatureBegin); + // lines.Add("*/"); + // lines.Add(line.Replace(unityLightFalloffNew, ftLightFalloffNew)); + // lines.Add(ftSignatureEnd); + // continue; + //} + //else if (line.IndexOf(unityLightFalloffNew2) >= 0) + //{ + // lines.Add(ftSignatureBegin); + // lines.Add("/*"); + // lines.Add(ftSignatureEnd); +// + // lines.Add(line); +// + // lines.Add(ftSignatureBegin); + // lines.Add("*/"); + // lines.Add(line.Replace(unityLightFalloffNew2, ftLightFalloffNew2)); + // lines.Add(ftSignatureEnd); + // continue; + //} + + if (line.IndexOf("#if") >= 0) lastIfdef = lines.Count; + if (line.IndexOf("define UNITY_LIGHT_ATTENUATION") >= 0 || line.IndexOf("define LIGHT_ATTENUATION") >= 0) + { + lastDefine = lines.Count; + } + if (line.IndexOf("#endif") >= 0) lastEndif = lines.Count; + + if (line.StartsWith(ftSignatureBegin)) + { + UnityEngine.Debug.Log("Already patched"); + patched = true; + break; + } + else + { + if (lastEndif == lines.Count && lastDefine > lastIfdef) // we should be at the endif of light atten declaration + { + string ifdefLine = lines[lastIfdef]; + string defineLine = lines[lastDefine]; + + if (defineLine.IndexOf("define UNITY_LIGHT_ATTENUATION") >= 0) + { + if ((ifdefLine.IndexOf("POINT") >= 0 || ifdefLine.IndexOf("SPOT") >= 0) && + ifdefLine.IndexOf("POINT_COOKIE") < 0 && ifdefLine.IndexOf("SPOT_COOKIE") < 0) + { + // Forward point light + lines.Insert(lastDefine, ftSignatureBegin); + lines.Insert(lastDefine + 1, "/*"); + lines.Insert(lastDefine + 2, ftSignatureEnd); + + lines.Add(ftSignatureBegin); + lines.Add("*/"); + + if (ifdefLine.IndexOf("POINT") >= 0) + { + //lines.Add(unityLightMatrixDecl); + lines.Add(unityDefineLightAtten + "\\"); + lines.Add(unityGetShadowCoord + "\\"); + lines.Add(unityGetShadow + "\\"); + lines.Add(ftLightFalloff + " * shadow;"); + } + else if (ifdefLine.IndexOf("SPOT") >= 0) + { + lines.Add(unityDefineLightAtten + "\\"); + lines.Add(unityGetShadowCoord4 + "\\"); + lines.Add(unityGetShadow + "\\"); + lines.Add(ftLightFalloff + " * (lightCoord.z > 0) * UnitySpotCookie(lightCoord) * shadow;"); + } + + lines.Add(ftSignatureEnd); + } + } + //else if (defineLine.IndexOf("define LIGHT_ATTENUATION") >= 0) + // { + // if (ifdefLine.IndexOf("POINT") >= 0) + // { + // // Deferred point light + // lines.Insert(lastDefine, ftSignatureBegin); + // lines.Insert(lastDefine + 1, "/*"); + // lines.Insert(lastDefine + 2, ftSignatureEnd); + + // lines.Insert(lastDefine + 4, ftSignatureBegin); + // lines.Insert(lastDefine + 5, "*/"); + + // if (ifdefLine.IndexOf("POINT") >= 0) + // { + // lines.Add(ftLightFalloffDeferred); + // } + + // lines.Add(ftSignatureEnd); + // } + // } + } + lines.Add(line); + } + } + reader.Close(); + + if (!patched) + { + if (!File.Exists(includeLightPath + "_backup")) File.Copy(includeLightPath, includeLightPath + "_backup"); + var writer = new StreamWriter(includeLightPath, false); + + if (writer == null) + { + UnityEngine.Debug.LogError("Can't open " + includeLightPath); + falloff = false; + return; + } + + for(int i=0; i(); + lines.Add(ftSignatureBegin); + lines.Add("#define USEFTRACE\n"); + lines.Add("#ifdef USEFTRACE"); + lines.Add("#include \"ftrace.cginc\""); + lines.Add("#endif"); + lines.Add(ftSignatureEnd); + + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line.StartsWith(ftSignatureBegin)) + { + UnityEngine.Debug.Log("Already patched"); + patched = true; + break; + } + else if (line.IndexOf(unitySpotFalloffDeferred) >= 0) + { + lines.Add(ftSignatureBegin); + lines.Add("/*"); + lines.Add(ftSignatureEnd); + + lines.Add(line); + + lines.Add(ftSignatureBegin); + lines.Add("*/"); + lines.Add(ftSpotFalloffDeferred); + lines.Add(ftSignatureEnd); + } + else if (line.IndexOf(unityPointFalloffDeferred) >= 0) + { + lines.Add(ftSignatureBegin); + lines.Add("/*"); + lines.Add(ftSignatureEnd); + + lines.Add(line); + + lines.Add(ftSignatureBegin); + lines.Add("*/"); + lines.Add(ftPointFalloffDeferred); + lines.Add(ftSignatureEnd); + } + else + { + lines.Add(line); + } + } + reader.Close(); + + if (!patched) + { + if (!File.Exists(includeDeferredPath + "_backup")) File.Copy(includeDeferredPath, includeDeferredPath + "_backup"); + var writer = new StreamWriter(includeDeferredPath, false); + + if (writer == null) + { + UnityEngine.Debug.LogError("Can't open " + includeDeferredPath); + falloffDeferred = false; + return; + } + + for(int i=0; i= 0) + { + if (cached != newVal) + { + BakerySkyLight.lightsChanged = 2; + } + } + cached = newVal; + } + + void TestPreviewRefreshProperty(ref int cached, UnityEngine.Object newVal) + { + if (newVal == null) + { + TestPreviewRefreshProperty(ref cached, 0); + return; + } + TestPreviewRefreshProperty(ref cached, newVal.GetInstanceID()); + } + + static string ftSkyboxShaderName = "Bakery/Skybox"; + + ftLightmapsStorage storage; + + static string[] selStrings = new string[] {"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16", + "17","18","19","20","21","22","23","24","25","26","27","28","29","30"};//,"31"}; + + static public string[] directContributionOptions = new string[] {"Direct And Indirect (recommended)", "Indirect only"}; + + bool showExperimental = false; + + void OnEnable() + { + ftraceLightColor = serializedObject.FindProperty("color"); + ftraceLightIntensity = serializedObject.FindProperty("intensity"); + ftraceLightIndirectIntensity = serializedObject.FindProperty("indirectIntensity"); + ftraceLightTexture = serializedObject.FindProperty("cubemap"); + ftraceLightSamples = serializedObject.FindProperty("samples"); + ftraceLightHemi = serializedObject.FindProperty("hemispherical"); + ftraceLightCorrectRot = serializedObject.FindProperty("correctRotation"); + ftraceLightBitmask = serializedObject.FindProperty("bitmask"); + ftraceLightBakeToIndirect = serializedObject.FindProperty("bakeToIndirect"); + ftraceTangentSH = serializedObject.FindProperty("tangentSH"); + } + + public override void OnInspectorGUI() { + { + serializedObject.Update(); + + TestPreviewRefreshProperty(ref texCached, ftraceLightTexture.objectReferenceValue); + + EditorGUILayout.PropertyField(ftraceLightColor, new GUIContent("Color", "Sky color. Multiplies texture color.")); + EditorGUILayout.PropertyField(ftraceLightIntensity, new GUIContent("Intensity", "Color multiplier")); + EditorGUILayout.PropertyField(ftraceLightTexture, new GUIContent("Sky texture", "Cubemap")); + if (ftraceLightTexture.objectReferenceValue != null) + { + EditorGUILayout.PropertyField(ftraceLightCorrectRot, new GUIContent("Correct rotation", "Enable to have a proper match between GameObject rotation and HDRI rotation. Disabled by default for backwards compatibility.")); + var angles = (target as BakerySkyLight).transform.eulerAngles; + EditorGUILayout.LabelField("Cubemap angles: " + angles.x + ", " + angles.y + ", " + angles.z); + EditorGUILayout.LabelField("Rotate this GameObject to change cubemap angles."); + EditorGUILayout.Space(); + } + EditorGUILayout.PropertyField(ftraceLightSamples, new GUIContent("Samples", "The amount of rays tested for this light. Rays are emitted hemispherically.")); + + EditorGUILayout.PropertyField(ftraceLightHemi, new GUIContent("Hemispherical", "Only emit light from upper hemisphere")); + + //ftraceLightBitmask.intValue = EditorGUILayout.MaskField(new GUIContent("Bitmask", "Lights only affect renderers with overlapping bits"), ftraceLightBitmask.intValue, selStrings); + int prevVal = ftraceLightBitmask.intValue; + int newVal = EditorGUILayout.MaskField(new GUIContent("Bitmask", "Lights only affect renderers with overlapping bits"), ftraceLightBitmask.intValue, selStrings); + if (prevVal != newVal) ftraceLightBitmask.intValue = newVal; + + //EditorGUILayout.PropertyField(ftraceLightBakeToIndirect, new GUIContent("Bake to indirect", "Add direct contribution from this light to indirect-only lightmaps")); + + if (storage == null) storage = ftRenderLightmap.FindRenderSettingsStorage(); + var rmode = storage.renderSettingsUserRenderMode; + if (rmode != (int)ftRenderLightmap.RenderMode.FullLighting) + { + ftDirectLightInspector.BakeWhat contrib; + if (ftraceLightBakeToIndirect.boolValue) + { + contrib = ftDirectLightInspector.BakeWhat.DirectAndIndirect; + } + else + { + contrib = ftDirectLightInspector.BakeWhat.IndirectOnly; + } + var prevContrib = contrib; + + contrib = (ftDirectLightInspector.BakeWhat)EditorGUILayout.Popup("Baked contribution", (int)contrib, directContributionOptions); + + if (prevContrib != contrib) + { + if (contrib == ftDirectLightInspector.BakeWhat.IndirectOnly) + { + ftraceLightBakeToIndirect.boolValue = false; + } + else + { + ftraceLightBakeToIndirect.boolValue = true; + } + } + } + + EditorGUILayout.PropertyField(ftraceLightIndirectIntensity, new GUIContent("Indirect intensity", "Non-physical GI multiplier for this light")); + + showExperimental = EditorGUILayout.Foldout(showExperimental, "Experimental", EditorStyles.foldout); + if (showExperimental) + { + EditorGUILayout.PropertyField(ftraceTangentSH, new GUIContent("Tangent-space SH", "Only affects single-color skylights. When baking in SH mode, harmonics will be in tangent space. Can be useful for implementing skinned model specular occlusion in custom shaders.")); + } + + serializedObject.ApplyModifiedProperties(); + } + + var skyMat = RenderSettings.skybox; + bool match = false; + bool skyboxValid = true; + string why = ""; + if (skyMat != null) + { + if (skyMat.HasProperty("_Tex") && skyMat.HasProperty("_Exposure") && skyMat.HasProperty("_Tint")) + { + if (skyMat.GetTexture("_Tex") == ftraceLightTexture.objectReferenceValue) + { + float exposure = skyMat.GetFloat("_Exposure"); + bool exposureSRGB = skyMat.shader.name == "Skybox/Cubemap"; + if (exposureSRGB) + { + exposure = Mathf.Pow(exposure, 2.2f); // can't detect [Gamma] keyword... + exposure *= PlayerSettings.colorSpace == ColorSpace.Linear ? 4.59f : 2; // weird unity constant + } + if (Mathf.Abs(exposure - ftraceLightIntensity.floatValue) < 0.0001f) + { + if (skyMat.GetColor("_Tint") == ftraceLightColor.colorValue) + { + bool anglesMatch = true; + var angles = (target as BakerySkyLight).transform.eulerAngles; + Vector3 matMatrixX = Vector3.right; + Vector3 matMatrixY = Vector3.up; + Vector3 matMatrixZ = Vector3.forward; + float matAngleY = 0; + bool hasYAngle = skyMat.HasProperty("_Rotation"); + bool hasXZAngles = skyMat.HasProperty("_MatrixRight"); + if (hasYAngle) matAngleY = skyMat.GetFloat("_Rotation"); + if (hasXZAngles) + { + matMatrixX = skyMat.GetVector("_MatrixRight"); + matMatrixY = skyMat.GetVector("_MatrixUp"); + matMatrixZ = skyMat.GetVector("_MatrixForward"); + } + + if (angles.y != 0 && !hasYAngle) + { + anglesMatch = false; + why = "no _Rotation property, but light is rotated"; + } + else if ((angles.x != 0 || angles.z != 0) && !hasXZAngles) + { + anglesMatch = false; + why = "shader doesn't allow XZ rotation"; + } + else + { + var lightQuat = (target as BakerySkyLight).transform.rotation; + Quaternion matQuat; + if (hasXZAngles) + { + var mtx = new Matrix4x4(); + mtx.SetColumn(0, new Vector4(matMatrixX.x, matMatrixX.y, matMatrixX.z, 0)); + mtx.SetColumn(1, new Vector4(matMatrixY.x, matMatrixY.y, matMatrixY.z, 0)); + mtx.SetColumn(2, new Vector4(matMatrixZ.x, matMatrixZ.y, matMatrixZ.z, 0)); + matQuat = QuaternionFromMatrix(mtx); + } + else + { + matQuat = Quaternion.Euler(0, matAngleY, 0); + } + + float diff = Quaternion.Angle(matQuat, lightQuat); + //Debug.Log("d " + diff); + if (Mathf.Abs(diff) > 0.01f) + { + anglesMatch = false; + why = "angles don't match"; + } + } + if (anglesMatch) match = true; + } + else + { + why = "color doesn't match"; + } + } + else + { + why = "exposure doesn't match"; + } + } + else + { + why = "texture doesn't match"; + } + } + else + { + if (!skyMat.HasProperty("_Tex")) why += "_Tex "; + if (!skyMat.HasProperty("_Exposure")) why += "_Exposure "; + if (!skyMat.HasProperty("_Tint")) why += "_Tint "; + why += "not defined"; + skyboxValid = false; + } + } + else + { + why = "no skybox set"; + skyboxValid = false; + } + + if (!match) + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Skylight doesn't match skybox: " + why); + EditorGUILayout.Space(); + + if (skyboxValid) + { + if (GUILayout.Button("Match this light to scene skybox")) + { + ftraceLightTexture.objectReferenceValue = skyMat.GetTexture("_Tex"); + + float exposure = skyMat.GetFloat("_Exposure"); + bool exposureSRGB = skyMat.shader.name == "Skybox/Cubemap"; + if (exposureSRGB) + { + exposure = Mathf.Pow(exposure, 2.2f); // can't detect [Gamma] keyword... + exposure *= PlayerSettings.colorSpace == ColorSpace.Linear ? 4.59f : 2; // weird unity constant + } + ftraceLightIntensity.floatValue = exposure; + + ftraceLightColor.colorValue = skyMat.GetColor("_Tint"); + + float matAngle = 0; + if (skyMat.HasProperty("_Rotation")) matAngle = skyMat.GetFloat("_Rotation"); + var matQuat = Quaternion.Euler(0, matAngle, 0); + Undo.RecordObject((target as BakerySkyLight).transform, "Rotate skylight"); + (target as BakerySkyLight).transform.rotation = matQuat; + + serializedObject.ApplyModifiedProperties(); + } + } + + if (GUILayout.Button("Match scene skybox to this light")) + { + var tform = (target as BakerySkyLight).transform; + var angles = tform.eulerAngles; + if (angles.x !=0 || angles.z !=0) + { + if (skyboxValid && !skyMat.HasProperty("_MatrixRight")) skyboxValid = false; // only ftrace skybox can handle xz rotation for now + } + + if (angles.y != 0 && skyboxValid && !skyMat.HasProperty("_Rotation")) skyboxValid = false; // needs _Rotation for Y angle + + if (!skyboxValid) + { + var outputPath = ftRenderLightmap.outputPath; + skyMat = new Material(Shader.Find(ftSkyboxShaderName)); + if (!Directory.Exists("Assets/" + outputPath)) + { + Directory.CreateDirectory("Assets/" + outputPath); + } + AssetDatabase.CreateAsset(skyMat, "Assets/" + outputPath + "/" + SceneManager.GetActiveScene().name + "_skybox.asset"); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + skyMat.SetTexture("_Tex", ftraceLightTexture.objectReferenceValue as Cubemap); + skyMat.SetFloat("_NoTexture", ftraceLightTexture.objectReferenceValue == null ? 1 : 0); + + float exposure = ftraceLightIntensity.floatValue; + bool exposureSRGB = skyMat.shader.name == "Skybox/Cubemap"; + if (exposureSRGB) + { + exposure /= PlayerSettings.colorSpace == ColorSpace.Linear ? 4.59f : 2; // weird unity constant + exposure = Mathf.Pow(exposure, 1.0f / 2.2f); // can't detect [Gamma] keyword... + } + skyMat.SetFloat("_Exposure", exposure); + + skyMat.SetColor("_Tint", ftraceLightColor.colorValue); + + if (skyMat.HasProperty("_Rotation")) skyMat.SetFloat("_Rotation", angles.y); + + if ((target as BakerySkyLight).correctRotation) + { + // transpose + var r = tform.right; + var u = tform.up; + var f = tform.forward; + if (skyMat.HasProperty("_MatrixRight")) skyMat.SetVector("_MatrixRight", new Vector3(r.x, u.x, f.x)); + if (skyMat.HasProperty("_MatrixUp")) skyMat.SetVector("_MatrixUp", new Vector3(r.y, u.y, f.y)); + if (skyMat.HasProperty("_MatrixForward")) skyMat.SetVector("_MatrixForward", new Vector3(r.z, u.z, f.z)); + } + else + { + if (skyMat.HasProperty("_MatrixRight")) skyMat.SetVector("_MatrixRight", tform.right); + if (skyMat.HasProperty("_MatrixUp")) skyMat.SetVector("_MatrixUp", tform.up); + if (skyMat.HasProperty("_MatrixForward")) skyMat.SetVector("_MatrixForward", tform.forward); + } + + RenderSettings.skybox = skyMat; + EditorUtility.SetDirty(skyMat); + } + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + } + } +} + + + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSkyLightInspector.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSkyLightInspector.cs.meta new file mode 100644 index 00000000..a8f7e985 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSkyLightInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 43b464df539471c47880a0cc39cd4861 +timeCreated: 1525278120 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftTextureProcessor.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftTextureProcessor.cs new file mode 100644 index 00000000..3f26bd6a --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftTextureProcessor.cs @@ -0,0 +1,57 @@ +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; + +public class ftTextureProcessor : AssetPostprocessor +{ + public static Dictionary texSettings = new Dictionary(); + static BakeryProjectSettings pstorage; + + public const int TEX_LM = 0; + public const int TEX_LMDEFAULT = 1; + public const int TEX_MASK = 2; + public const int TEX_DIR = 3; + + void OnPreprocessTexture() + { + TextureImporter importer = assetImporter as TextureImporter; + Vector2 settings; + + if (!texSettings.TryGetValue(importer.assetPath, out settings)) return; + + if (pstorage == null) pstorage = ftLightmaps.GetProjectSettings(); + + importer.maxTextureSize = (int)settings.x; + importer.mipmapEnabled = pstorage.mipmapLightmaps; + importer.wrapMode = TextureWrapMode.Clamp; + + int texType = (int)settings.y; + switch(texType) + { + case TEX_LM: + { + importer.textureType = TextureImporterType.Lightmap; + break; + } + case TEX_LMDEFAULT: + { + importer.textureType = TextureImporterType.Default; + break; + } + case TEX_MASK: + { + importer.textureType = TextureImporterType.Default; + importer.textureCompression = TextureImporterCompression.CompressedHQ; + break; + } + case TEX_DIR: + { + importer.textureType = TextureImporterType.Default; + importer.textureCompression = TextureImporterCompression.CompressedHQ; + importer.sRGBTexture = (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG); + break; + } + } + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftTextureProcessor.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftTextureProcessor.cs.meta new file mode 100644 index 00000000..9fe9fda4 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftTextureProcessor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 68d2ca0e99ca9604fa09956f75773620 +timeCreated: 1546597706 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUVGBufferGen.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUVGBufferGen.cs new file mode 100644 index 00000000..368d17d9 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUVGBufferGen.cs @@ -0,0 +1,595 @@ +#if UNITY_EDITOR + +using UnityEngine; +using UnityEditor; +using System.IO; + +public class ftUVGBufferGen +{ + static RenderTexture rtAlbedo, rtEmissive, rtNormal, rtAlpha; + public static Texture2D texAlbedo, texEmissive, texNormal, texBestFit, texAlpha; + //static GameObject dummyCamGO; + //static Camera dummyCam; + static float texelSize; + //static Vector4 shaBlack, shaWhite; + static Material matFromRGBM; + static Material matDilate, matMultiply; + static bool emissiveEnabled = false; + static bool normalEnabled = false; + static bool alphaEnabled = false; + static Vector4 metaControl, metaControlAlbedo, metaControlEmission, metaControlNormal, metaControlAlpha; + static Material fallbackMat, normalMat, blackMat; + static int fallbackMatMetaPass; + static BakeryProjectSettings pstorage; + + const int PASS_ALBEDO = 0; + const int PASS_EMISSIVE = 1; + const int PASS_NORMAL = 2; + const int PASS_ALPHA = 3; + const int PASS_COUNT = 4; // just a marker + + public static float[] uvOffset = + { + -2, -2, + 2, -2, + -2, 2, + 2, 2, + + -1, -2, + 1, -2, + -2, -1, + 2, -1, + -2, 1, + 2, 1, + -1, 2, + 1, 2, + + -2, 0, + 2, 0, + 0, -2, + 0, 2, + + -1, -1, + 1, -1, + -1, 0, + 1, 0, + -1, 1, + 1, 1, + 0, -1, + 0, 1, + + 0, 0 + }; + + static public void UpdateMatrix(Matrix4x4 worldMatrix, float offsetX, float offsetY)//Matrix4x4 worldMatrix) + { + // Generate a projection matrix similar to LoadOrtho + /*var dummyCamGO = new GameObject(); + dummyCamGO.name = "dummyCam"; + var dummyCam = dummyCamGO.AddComponent(); + dummyCam.cullingMask = 0; + dummyCam.orthographic = true; + dummyCam.orthographicSize = 0.5f; + dummyCam.nearClipPlane = -10; + dummyCam.aspect = 1; + var proj = dummyCam.projectionMatrix; + var c3 = proj.GetColumn(3); + proj.SetColumn(3, new Vector4(-1, -1, c3.z, c3.w)); + Debug.Log(proj);*/ + + var proj = new Matrix4x4(); + proj.SetRow(0, new Vector4(2.00000f, 0.00000f, 0.00000f, -1.00000f + offsetX)); + proj.SetRow(1, new Vector4(0.00000f, 2.00000f, 0.00000f, -1.00000f + offsetY)); + proj.SetRow(2, new Vector4(0.00000f, 0.00000f, -0.00198f, -0.98f)); + proj.SetRow(3, new Vector4(0.00000f, 0.00000f, 0.00000f, 1.00000f)); + + //if (ftBuildGraphics.unityVersionMajor < 2018) // Unity 2018 stopped multiplying vertices by world matrix in meta pass + //{ +#if UNITY_2018_1_OR_NEWER +#else + proj = proj * worldMatrix.inverse; +#endif + //} + + // If Camera.current is set, multiply our matrix by the inverse of its view matrix + if (Camera.current != null) + { + proj = proj * Camera.current.worldToCameraMatrix.inverse; + } + + GL.LoadProjectionMatrix(proj); + } + + static public void StartUVGBuffer(int size, bool hasEmissive, bool hasNormal) + { + emissiveEnabled = hasEmissive; + normalEnabled = hasNormal; + alphaEnabled = false; + + rtAlbedo = new RenderTexture(size, size, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); + texAlbedo = new Texture2D(size, size, TextureFormat.RGBA32, false, false); + + Graphics.SetRenderTarget(rtAlbedo); + GL.Clear(true, true, new Color(0,0,0,0)); + + if (hasEmissive) + { + rtEmissive = new RenderTexture(size, size, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear); + texEmissive = new Texture2D(size, size, TextureFormat.RGBAHalf, false, true); + Graphics.SetRenderTarget(rtEmissive); + GL.Clear(true, true, new Color(0,0,0,0)); + } + + if (hasNormal) + { + rtNormal = new RenderTexture(size, size, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); + texNormal = new Texture2D(size, size, TextureFormat.RGBA32, false, true); + Graphics.SetRenderTarget(rtNormal); + GL.Clear(true, true, new Color(0,0,0,0)); + } + + //GL.sRGBWrite = true;//!hasEmissive; + GL.invertCulling = false; + GL.PushMatrix(); + //GL.LoadOrtho(); + //UpdateMatrix(); + /*float ambR, ambG, ambB; + //ambR = ambG = ambB = emissiveOnly ? 0 : 1; + Shader.SetGlobalVector("unity_SHBr", Vector4.zero); + Shader.SetGlobalVector("unity_SHBg", Vector4.zero); + Shader.SetGlobalVector("unity_SHBb", Vector4.zero); + Shader.SetGlobalVector("unity_SHC", Vector4.zero);*/ + texelSize = (1.0f / size) / 5; + //shaBlack = new Vector4(0,0,0,0); + //shaWhite = new Vector4(0,0,0,1); + metaControl = new Vector4(1,0,0,0); + metaControlAlbedo = new Vector4(1,0,0,0); + metaControlEmission = new Vector4(0,1,0,0); + metaControlNormal = new Vector4(0,0,1,0); + metaControlAlpha = new Vector4(0,0,0,1); + Shader.SetGlobalVector("unity_MetaVertexControl", metaControl); + Shader.SetGlobalFloat("unity_OneOverOutputBoost", 1.0f); + Shader.SetGlobalFloat("unity_MaxOutputValue", 10000000.0f); + Shader.SetGlobalFloat("unity_UseLinearSpace", PlayerSettings.colorSpace == ColorSpace.Linear ? 1.0f : 0.0f); + } + + static public void InitAlphaBuffer(int size) + { + alphaEnabled = true; + rtAlpha = new RenderTexture(size, size, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); + texAlpha = new Texture2D(size, size, TextureFormat.RGBA32, false, true); + Graphics.SetRenderTarget(rtAlpha); + GL.Clear(true, true, new Color(0,0,0,0)); + } + + static public void RenderUVGBuffer(Mesh mesh, Renderer renderer, Vector4 scaleOffset, Transform worldTransform, bool vertexBake, + Vector2[] uvOverride, bool terrainNormals = false, bool metaAlpha = false) + { + var worldMatrix = worldTransform.localToWorldMatrix; + + if (pstorage == null) pstorage = ftLightmaps.GetProjectSettings(); + + if (metaAlpha && !alphaEnabled) + { + int res = rtAlbedo.width * pstorage.alphaMetaPassResolutionMultiplier; + if (res > 8192) res = 8192; + InitAlphaBuffer(res); + } + + Material[] materials = renderer.sharedMaterials; +#if SUPPORT_MBLOCKS + var mb = new MaterialPropertyBlock(); +#endif + + var m = mesh; + if (uvOverride != null) + { + m = Mesh.Instantiate(mesh); + //var uvs = m.uv2; + //if (uvs.Length == 0) uvs = m.uv; + //var pos = new Vector3[uvs.Length]; + /*for(int i=0; i= PASS_NORMAL) isHDRP = false; // custom meta shaders are not affected + int bakeryPass = -1; + + if (pass < PASS_NORMAL) + { + int metaPass = -1; + if (!materials[i].HasProperty("BAKERY_FORCE_NO_META")) + { + if (!passAsBlack) + { + metaPass = materials[i].FindPass("META"); + if (metaPass < 0) + { + // Try finding another pass pass with "META" in it + for(int mpass=0; mpass= 0) + { + metaPass = mpass; + break; + } + } + } + } + } + Shader.SetGlobalVector("unity_LightmapST", (isHDRP) ? scaleOffsetFlipped : scaleOffset); + Shader.SetGlobalVector("unity_MetaFragmentControl", pass == PASS_ALBEDO ? metaControlAlbedo : metaControlEmission); + + if (metaPass >= 0) + { + materials[i].SetPass(metaPass); + } + else + { + if (passAsBlack) + { + if (blackMat == null) + { + blackMat = new Material(Shader.Find("Hidden/ftBlack")); + } + Shader.SetGlobalVector("unity_LightmapST", scaleOffset); + blackMat.SetPass(0); + } + else + { + if (fallbackMat == null) + { + fallbackMat = new Material(Shader.Find("Standard")); + fallbackMat.EnableKeyword("_EMISSION"); + fallbackMatMetaPass = fallbackMat.FindPass("META"); + } + if ((pstorage.logLevel & (int)BakeryProjectSettings.LogLevel.Warning) != 0) + { + if (materials[i].name != "Hidden/ftFarSphere") + { + Debug.LogWarning("Material " + materials[i].name + " doesn't have meta pass - maps are taken by name"); + } + } + if (materials[i].HasProperty("_MainTex")) + { + fallbackMat.mainTexture = materials[i].GetTexture("_MainTex"); + } + else if (materials[i].HasProperty("_BaseColorMap")) + { + // HDRP + fallbackMat.mainTexture = materials[i].GetTexture("_BaseColorMap"); + } + else if (materials[i].HasProperty("_BaseMap")) + { + // URP + fallbackMat.mainTexture = materials[i].GetTexture("_BaseMap"); + } + if (materials[i].HasProperty("_Color")) + { + fallbackMat.SetVector("_Color", materials[i].GetVector("_Color")); + } + else + { + fallbackMat.SetVector("_Color", Color.white); + } + if (materials[i].HasProperty("_EmissionMap")) + { + fallbackMat.SetTexture("_EmissionMap", materials[i].GetTexture("_EmissionMap")); + } + else + { + fallbackMat.SetTexture("_EmissionMap", null); + } + if (materials[i].HasProperty("_EmissionColor")) + { + fallbackMat.SetVector("_EmissionColor", materials[i].GetVector("_EmissionColor")); + } + else + { + fallbackMat.SetVector("_EmissionColor", Color.black); + } + fallbackMat.SetPass(fallbackMatMetaPass); + } + } + } + else if (pass == PASS_NORMAL) + { + bool isURP = rpTag == "UniversalPipeline"; + + var metaPass = materials[i].FindPass("META_BAKERY"); + bakeryPass = metaPass; + + if (normalMat == null && metaPass < 0) + { + normalMat = new Material(Shader.Find("Hidden/ftUVNormalMap")); + } + if (texBestFit == null) + { + texBestFit = new Texture2D(1024, 1024, TextureFormat.RGBA32, false, true); + var edPath = ftLightmaps.GetEditorPath(); + var fbestfit = new BinaryReader(File.Open(edPath + "NormalsFittingTexture_dds", FileMode.Open, FileAccess.Read)); + fbestfit.BaseStream.Seek(128, SeekOrigin.Begin); + var bytes = fbestfit.ReadBytes(1024 * 1024 * 4); + fbestfit.Close(); + texBestFit.LoadRawTextureData(bytes); + texBestFit.Apply(); + } + + if (metaPass < 0) + { + if (materials[i].HasProperty("_BumpMap")) + { + normalMat.SetTexture("_BumpMap", materials[i].GetTexture("_BumpMap")); + if (materials[i].HasProperty("_MainTex_ST")) + { + normalMat.SetVector("_BumpMap_scaleOffset", materials[i].GetVector("_MainTex_ST")); + //Debug.LogError(materials[i].GetVector("_MainTex_ST")); + } + else + { + normalMat.SetVector("_BumpMap_scaleOffset", new Vector4(1,1,0,0)); + } + } + else if (materials[i].HasProperty("_NormalMap")) + { + normalMat.SetTexture("_BumpMap", materials[i].GetTexture("_NormalMap")); + normalMat.SetVector("_BumpMap_scaleOffset", new Vector4(1,1,0,0)); + } + else + { + normalMat.SetTexture("_BumpMap", null); + } + normalMat.SetFloat("_IsTerrain", terrainNormals ? 1.0f : 0.0f); + normalMat.SetTexture("bestFitNormalMap", texBestFit); + normalMat.SetFloat("_IsPerPixel", (isURP||isHDRP) ? 1.0f : 0.0f); + normalMat.SetPass(0); + } + else + { + materials[i].SetTexture("bestFitNormalMap", texBestFit); + materials[i].SetFloat("_IsPerPixel", (isURP||isHDRP) ? 1.0f : 0.0f); + materials[i].SetPass(metaPass); + } + Shader.SetGlobalVector("unity_MetaFragmentControl", metaControlNormal); + } + else if (pass == PASS_ALPHA) + { + // Unity does not output alpha in its meta pass, so only custom shaders are supported + var metaPass = materials[i].FindPass("META_BAKERY"); + if (metaPass < 0) + { + Debug.LogError("BAKERY_META_ALPHA_ENABLE is set, but there is no META_BAKERY pass in " + materials[i].name); + continue; + } + bakeryPass = metaPass; + materials[i].SetPass(metaPass); + Shader.SetGlobalVector("unity_MetaFragmentControl", metaControlAlpha); + } + + GL.sRGBWrite = pass == PASS_ALBEDO; + + if (!vertexBake) + { + for(int j=0; j= 0) + { + materials[i].SetPass(bakeryPass); + } + else + { + var s = worldTransform.lossyScale; + bool isFlipped = Mathf.Sign(s.x*s.y*s.z) < 0; + normalMat.SetFloat("_IsFlipped", isFlipped ? -1.0f : 1.0f); + normalMat.SetPass(0); + } + } + Graphics.DrawMeshNow(m, worldMatrix, i); + } + } + else + { + UpdateMatrix(worldMatrix, 0, 0); +#if SUPPORT_MBLOCKS + #if UNITY_2018_1_OR_NEWER + renderer.GetPropertyBlock(mb, i); + #else + renderer.GetPropertyBlock(mb); + #endif + Graphics.DrawMesh(m, worldMatrix, materials[i], 0, null, i, mb, false, false, false); +#else + Graphics.DrawMeshNow(m, worldMatrix, i); +#endif + } + } + } + } + + static public void EndUVGBuffer() + { + GL.PopMatrix(); + + Graphics.SetRenderTarget(rtAlbedo); + texAlbedo.ReadPixels(new Rect(0,0,rtAlbedo.width,rtAlbedo.height), 0, 0, false); + texAlbedo.Apply(); + Graphics.SetRenderTarget(null); + rtAlbedo.Release(); + + if (emissiveEnabled) + { + Graphics.SetRenderTarget(rtEmissive); + texEmissive.ReadPixels(new Rect(0,0,rtEmissive.width,rtEmissive.height), 0, 0, false); + texEmissive.Apply(); + Graphics.SetRenderTarget(null); + rtEmissive.Release(); + } + + if (normalEnabled) + { + Graphics.SetRenderTarget(rtNormal); + texNormal.ReadPixels(new Rect(0,0,rtNormal.width,rtNormal.height), 0, 0, false); + texNormal.Apply(); + Graphics.SetRenderTarget(null); + rtNormal.Release(); + } + + if (alphaEnabled) + { + Graphics.SetRenderTarget(rtAlpha); + texAlpha.ReadPixels(new Rect(0,0,rtAlpha.width,rtAlpha.height), 0, 0, false); + texAlpha.Apply(); + Graphics.SetRenderTarget(null); + rtAlpha.Release(); + } + } + + static public Texture2D DecodeFromRGBM(Texture2D emissive) + { + var rt = new RenderTexture(emissive.width, emissive.height, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear); + var tex = new Texture2D(emissive.width, emissive.height, TextureFormat.RGBAHalf, false, true); + + if (matFromRGBM == null) matFromRGBM = new Material(Shader.Find("Hidden/ftRGBM2Half")); + + Graphics.SetRenderTarget(rt); + GL.sRGBWrite = false; + + matFromRGBM.SetTexture("_MainTex", emissive); + + Graphics.Blit(emissive, rt, matFromRGBM); + + tex.ReadPixels(new Rect(0,0,rt.width,rt.height), 0, 0, false); + tex.Apply(); + + Graphics.SetRenderTarget(null); + rt.Release(); + Object.DestroyImmediate(emissive); + + return tex; + } + + static public void Dilate(Texture2D albedo) + { + if (matDilate == null) matDilate = new Material(Shader.Find("Hidden/ftDilate")); + + RenderTexture rt, rt2; + if (albedo.format == TextureFormat.RGBA32) + { + rt = new RenderTexture(albedo.width, albedo.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); + rt2 = new RenderTexture(albedo.width, albedo.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); + } + else + { + rt = new RenderTexture(albedo.width, albedo.height, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear); + rt2 = new RenderTexture(albedo.width, albedo.height, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear); + } + + GL.sRGBWrite = albedo.format == TextureFormat.RGBA32; + Graphics.Blit(albedo, rt, matDilate); + + for(int i=0; i<8; i++) + { + Graphics.Blit(rt, rt2, matDilate); + Graphics.Blit(rt2, rt, matDilate); + } + + Graphics.SetRenderTarget(rt); + albedo.ReadPixels(new Rect(0,0,rt.width,rt.height), 0, 0, false); + albedo.Apply(); + + Graphics.SetRenderTarget(null); + rt.Release(); + rt2.Release(); + } + + static public void Multiply(Texture2D albedo, float val) + { + if (matMultiply == null) matMultiply = new Material(Shader.Find("Hidden/ftMultiply")); + + RenderTexture rt; + if (albedo.format == TextureFormat.RGBA32) + { + rt = new RenderTexture(albedo.width, albedo.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); + } + else + { + rt = new RenderTexture(albedo.width, albedo.height, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear); + } + + GL.sRGBWrite = albedo.format == TextureFormat.RGBA32; + matMultiply.SetFloat("multiplier", val); + Graphics.Blit(albedo, rt, matMultiply); + + Graphics.SetRenderTarget(rt); + albedo.ReadPixels(new Rect(0,0,rt.width,rt.height), 0, 0, false); + albedo.Apply(); + + Graphics.SetRenderTarget(null); + rt.Release(); + } +} + +#endif + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUVGBufferGen.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUVGBufferGen.cs.meta new file mode 100644 index 00000000..8e5647b0 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUVGBufferGen.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 5d93843fab7110948a9eba95e82ced39 +timeCreated: 1533369120 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUpdater.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUpdater.cs new file mode 100644 index 00000000..4e2b2f00 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUpdater.cs @@ -0,0 +1,378 @@ +// Disable 'obsolete' warnings +#pragma warning disable 0618 + +#if UNITY_EDITOR + +using UnityEngine; +using UnityEditor; +using UnityEditor.Callbacks; +using System; +using System.IO; +using System.Text; +using System.Net; +using System.Collections; +using System.Collections.Generic; +using UnityEngine.Networking; +using System.Runtime.InteropServices; + +[InitializeOnLoad] +public class ftUpdater : EditorWindow +{ + [DllImport ("frender", CallingConvention=CallingConvention.Cdecl)] + public static extern int ExtractZIP([MarshalAs(UnmanagedType.LPWStr)]string zipFilename, int skipInnerFolders, string onlyFolder, [MarshalAs(UnmanagedType.LPWStr)]string outPath); + + IEnumerator progressFunc; + float progress = 0.0f; + string curItem = ""; + bool isError = false; + + string inLM = "IN000000000000"; + string inRT = "IN000000000000"; + string username = ""; + string errMsg = ""; + string lastVer = ""; + bool init = false; + + [MenuItem ("Bakery/Utilities/Check for patches", false, 1000)] + public static void Check() + { + var instance = (ftUpdater)GetWindow(typeof(ftUpdater)); + instance.titleContent.text = "Bakery patch"; + instance.minSize = new Vector2(320, 110); + instance.maxSize = new Vector2(instance.minSize.x, instance.minSize.y + 1); + instance.Show(); + } + + void DebugLogError(string str) + { + Debug.LogError(str); + errMsg = str; + progressFunc = null; + isError = true; + Repaint(); + } + + IEnumerator DownloadItem(string url) + { + var req = UnityWebRequest.Get(url + curItem); + yield return req.Send(); + while(!req.isDone) + { + progress = req.downloadProgress; + Repaint(); + yield return null; + } + + if (req.isError) + { + DebugLogError("Download error (" + curItem + ")"); + yield break; + } + else + { + if (req.downloadHandler.data.Length < 100) + { + DebugLogError(req.downloadHandler.text); + yield break; + } + else + { + File.WriteAllBytes(curItem + ".zip", req.downloadHandler.data); + } + } + } + + IEnumerator GetLastVer(string url) + { + lastVer = ""; + var req = UnityWebRequest.Get(url + curItem + "&getLastVer"); + yield return req.Send(); + while(!req.isDone) + { + progress = req.downloadProgress; + Repaint(); + yield return null; + } + + if (req.isError) + { + DebugLogError("Request error (" + curItem + ")"); + yield break; + } + else + { + if (req.downloadHandler.data.Length != 40) + { + DebugLogError(req.downloadHandler.text); + yield break; + } + else + { + lastVer = req.downloadHandler.text; + } + } + } + + IEnumerator DownloadItemIfNewer(string url) + { + var dw = GetLastVer(url); + while(dw.MoveNext()) yield return null; + if (isError) yield break; + + var fname = curItem + "-cver.txt"; // currently installed + if (File.Exists(fname)) + { + var curVer = File.ReadAllText(fname); + if (lastVer == curVer) + { + Debug.Log(curItem + ": already latest"); + yield break; + } + } + + dw = DownloadItem(url); + while(dw.MoveNext()) yield return null; + + File.WriteAllText(curItem + "-dver.txt", lastVer); // downloaded + } + + IEnumerator CheckProc() + { + //var runtimePath = ftLightmaps.GetRuntimePath(); + //var editorPath = ftLightmaps.GetEditorPath(); + + isError = false; + + bool downloadLM = inLM.Length > 0 && inLM != "IN000000000000"; + bool downloadRT = inRT.Length > 0 && inRT != "IN000000000000"; + + if (!downloadLM && !downloadRT) + { + DebugLogError("No invoices set"); + yield break; + } + + if (downloadLM) + { + // Download bakery-csharp + curItem = "bakery-csharp"; + var dw = DownloadItemIfNewer("https://geom.io/bakery/github-download.php?name=" + username + "&invoice=" + inLM + "&repo="); + while(dw.MoveNext()) yield return null; + if (isError) yield break; + + // Download bakery-compiled + curItem = "bakery-compiled"; + dw = DownloadItemIfNewer("https://geom.io/bakery/github-download.php?name=" + username + "&invoice=" + inLM + "&repo="); + while(dw.MoveNext()) yield return null; + if (isError) yield break; + } + + if (downloadRT) + { + // Download bakery-rtpreview-csharp + curItem = "bakery-rtpreview-csharp"; + var dw = DownloadItemIfNewer("https://geom.io/bakery/github-download.php?name=" + username + "&invoice=" + inRT + "&repo="); + while(dw.MoveNext()) yield return null; + if (isError) yield break; + } + + var cachePath = Directory.GetCurrentDirectory() + "/BakeryPatchCache"; + if (!Directory.Exists(cachePath)) Directory.CreateDirectory(cachePath); + + var runtimePath = cachePath + "/Runtime"; + if (!Directory.Exists(runtimePath)) Directory.CreateDirectory(runtimePath); + + var editorPath = cachePath + "/Editor"; + if (!Directory.Exists(editorPath)) Directory.CreateDirectory(editorPath); + + if (downloadLM) + { + // Extract runtime files + int err = ExtractZIP("bakery-csharp.zip", 1, "Bakery", runtimePath); + if (err != 0) + { + DebugLogError("ExtractZIP: " + err); + yield break; + } + + // Extract editor files + err = ExtractZIP("bakery-csharp.zip", 3, "Bakery", editorPath); + if (err != 0) + { + DebugLogError("ExtractZIP: " + err); + yield break; + } + + Debug.Log("Extracted bakery-csharp"); + + // Extract binaries + err = ExtractZIP("bakery-compiled.zip", 1, "", editorPath); + if (err != 0) + { + DebugLogError("ExtractZIP: " + err); + yield break; + } + + Debug.Log("Extracted bakery-compiled"); + } + + if (downloadRT) + { + // Extract RTPreview files + int err = ExtractZIP("bakery-rtpreview-csharp.zip", 1, "", editorPath); + if (err != 0) + { + DebugLogError("ExtractZIP: " + err); + yield break; + } + + Debug.Log("Extracted bakery-rtpreview-csharp"); + } + + Debug.Log("Done"); + + progressFunc = null; + Repaint(); + EditorUtility.DisplayDialog("Bakery", "Restart Editor to apply the patch", "OK"); + } + + void CheckUpdate() + { + if (!progressFunc.MoveNext()) + { + EditorApplication.update -= CheckUpdate; + } + } + + void OnGUI() + { + if (!init) + { + if (PlayerPrefs.HasKey("BakeryInvLM")) inLM = PlayerPrefs.GetString("BakeryInvLM"); + if (PlayerPrefs.HasKey("BakeryInvRT")) inRT = PlayerPrefs.GetString("BakeryInvRT"); + if (PlayerPrefs.HasKey("BakeryGHUsername")) username = PlayerPrefs.GetString("BakeryGHUsername"); + init = true; + } + + int y = 10; + + if (progressFunc != null) GUI.enabled = false; + + GUI.Label(new Rect(5, y, 130, 18), "Lightmapper invoice:"); + var prev = inLM; + inLM = EditorGUI.TextField(new Rect(140, y, 170, 18), inLM); + if (inLM != prev && (inLM.Length == 14 || inLM.Length == 0)) + { + PlayerPrefs.SetString("BakeryInvLM", inLM); + } + y += 18; + + GUI.Label(new Rect(5, y, 120, 18), "RTPreview invoice:"); + prev = inRT; + inRT = EditorGUI.TextField(new Rect(140, y, 170, 18), inRT); + if (inRT != prev && (inRT.Length == 14 || inRT.Length == 0)) + { + PlayerPrefs.SetString("BakeryInvRT", inRT); + } + y += 18; + + GUI.Label(new Rect(5, y, 130, 18), "GitHub username:"); + prev = username; + username = EditorGUI.TextField(new Rect(140, y, 170, 18), username); + if (username != prev && username.Length <= 255) + { + PlayerPrefs.SetString("BakeryGHUsername", username); + } + y += 18*2; + + if (GUI.Button(new Rect(0, y, 320, 18), "Check")) + { + SessionState.SetBool("BakeryPatchWaitForRestart", true); + progressFunc = CheckProc(); + EditorApplication.update += CheckUpdate; + } + y += 20; + + GUI.enabled = true; + + minSize = new Vector2(320, isError ? 160 : (progressFunc == null ? 110 : 160)); + + if (progressFunc != null) + { + GUI.Label(new Rect(0, y, 320, 24), curItem); + y += 24; + EditorGUI.ProgressBar(new Rect(0, y, 320, 24), progress, progress > 0 ? ("Downloading: " + (int)(progress * 100) + "%") : "Waiting for server..."); + } + else if (isError) + { + EditorGUI.HelpBox(new Rect(0, y, 320, 40), errMsg, MessageType.Error); + } + } + + private static void Copy(string srcFolder, string destFolder) + { + var dir = new DirectoryInfo(srcFolder); + if (!dir.Exists) + { + Debug.LogError("Can't find " + srcFolder); + return; + } + + Directory.CreateDirectory(destFolder); + + var files = dir.GetFiles(); + foreach (FileInfo file in files) + { + string tempPath = Path.Combine(destFolder, file.Name); + file.CopyTo(tempPath, true); + } + + var dirs = dir.GetDirectories(); + foreach (DirectoryInfo subdir in dirs) + { + string tempPath = Path.Combine(destFolder, subdir.Name); + Copy(subdir.FullName, tempPath); + + Debug.Log("Copying " + tempPath); + } + } + + static void PatchAsk() + { + EditorApplication.update -= PatchAsk; + + if (Application.isPlaying) return; + + // Run only once when opening the editor (not when reloading scripts, changing between modes, etc) + if (SessionState.GetBool("BakeryPatchWaitForRestart", false)) return; + + var cachePath = Directory.GetCurrentDirectory() + "/BakeryPatchCache"; + + if (EditorUtility.DisplayDialog("Bakery", "Bakery patch was downloaded. Apply patch?", "Yes", "No")) + { + Copy(cachePath + "/Runtime", ftLightmaps.GetRuntimePath()); + Copy(cachePath + "/Editor", ftLightmaps.GetEditorPath()); + + // Downloaded version -> current version + if (File.Exists("bakery-csharp-dver.txt")) File.Copy("bakery-csharp-dver.txt", "bakery-csharp-cver.txt", true); + if (File.Exists("bakery-compiled-dver.txt")) File.Copy("bakery-compiled-dver.txt", "bakery-compiled-cver.txt", true); + if (File.Exists("bakery-rtpreview-csharp-dver.txt")) File.Copy("bakery-rtpreview-csharp-dver.txt", "bakery-rtpreview-csharp-cver.txt", true); + } + + Directory.Delete(cachePath, true); + + AssetDatabase.Refresh(); + } + + static ftUpdater() + { + // Was the patch downloaded? + var cachePath = Directory.GetCurrentDirectory() + "/BakeryPatchCache"; + if (!Directory.Exists(cachePath)) return; + + // Can't call everything in the constructor, continue there + EditorApplication.update += PatchAsk; + } +} + +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUpdater.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUpdater.cs.meta new file mode 100644 index 00000000..a2550400 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftUpdater.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7150afb8f1312c144ab37ad7b22c6e7a +timeCreated: 1618931374 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftVolumeInspector.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftVolumeInspector.cs new file mode 100644 index 00000000..acba7105 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftVolumeInspector.cs @@ -0,0 +1,226 @@ +// Disable 'obsolete' warnings +#pragma warning disable 0618 + +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.IMGUI.Controls; +#endif + +#if UNITY_EDITOR +[CustomEditor(typeof(BakeryVolume))] +public class BakeryVolumeInspector : Editor +{ + BoxBoundsHandle boundsHandle = new BoxBoundsHandle(typeof(BakeryVolumeInspector).GetHashCode()); + + SerializedProperty ftraceAdaptiveRes, ftraceResX, ftraceResY, ftraceResZ, ftraceVoxelsPerUnit, ftraceAdjustSamples, ftraceEnableBaking, ftraceEncoding, ftraceShadowmaskEncoding, ftraceDenoise, ftraceGlobal, ftraceRotation; + + bool showExperimental = false; + + ftLightmapsStorage storage; + + static BakeryProjectSettings pstorage; + + void OnEnable() + { + ftraceAdaptiveRes = serializedObject.FindProperty("adaptiveRes"); + ftraceVoxelsPerUnit = serializedObject.FindProperty("voxelsPerUnit"); + ftraceResX = serializedObject.FindProperty("resolutionX"); + ftraceResY = serializedObject.FindProperty("resolutionY"); + ftraceResZ = serializedObject.FindProperty("resolutionZ"); + ftraceEnableBaking = serializedObject.FindProperty("enableBaking"); + ftraceEncoding = serializedObject.FindProperty("encoding"); + ftraceShadowmaskEncoding = serializedObject.FindProperty("shadowmaskEncoding"); + ftraceDenoise = serializedObject.FindProperty("denoise"); + ftraceGlobal = serializedObject.FindProperty("isGlobal"); + ftraceRotation = serializedObject.FindProperty("supportRotationAfterBake"); + //ftraceAdjustSamples = serializedObject.FindProperty("adjustSamples"); + } + + string F(float f) + { + // Unity keeps using comma for float printing on some systems since ~2018, even if system-wide decimal symbol is "." + return (f + "").Replace(",", "."); + } + + string FormatSize(int b) + { + float mb = b / (float)(1024*1024); + return mb.ToString("0.0"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + var vol = target as BakeryVolume; + + if (pstorage == null) pstorage = ftLightmaps.GetProjectSettings(); + + EditorGUILayout.PropertyField(ftraceEnableBaking, new GUIContent("Enable baking", "Should the volume be (re)computed? Disable to prevent overwriting existing data.")); + bool wasGlobal = ftraceGlobal.boolValue; + EditorGUILayout.PropertyField(ftraceGlobal, new GUIContent("Global", "Automatically assign this volume to all volume-compatible shaders, unless they have overrides.")); + if (!wasGlobal && ftraceGlobal.boolValue) + { + (target as BakeryVolume).SetGlobalParams(); + } + EditorGUILayout.PropertyField(ftraceDenoise, new GUIContent("Denoise", "Apply denoising after baking the volume.")); + EditorGUILayout.Space(); + + EditorGUILayout.PropertyField(ftraceAdaptiveRes, new GUIContent("Adaptive resolution", "Calculate voxel resolution based on size?")); + if (ftraceAdaptiveRes.boolValue) + { + EditorGUILayout.PropertyField(ftraceVoxelsPerUnit, new GUIContent("Voxels per unit")); + + GUI.enabled = false; + var size = vol.bounds.size; + ftraceResX.intValue = System.Math.Max((int)(size.x * vol.voxelsPerUnit), 1); + ftraceResY.intValue = System.Math.Max((int)(size.y * vol.voxelsPerUnit), 1); + ftraceResZ.intValue = System.Math.Max((int)(size.z * vol.voxelsPerUnit), 1); + } + EditorGUILayout.PropertyField(ftraceResX, new GUIContent("Resolution X")); + EditorGUILayout.PropertyField(ftraceResY, new GUIContent("Resolution Y")); + EditorGUILayout.PropertyField(ftraceResZ, new GUIContent("Resolution Z")); + if (ftraceResX.intValue < 1) ftraceResX.intValue = 1; + if (ftraceResY.intValue < 1) ftraceResY.intValue = 1; + if (ftraceResZ.intValue < 1) ftraceResZ.intValue = 1; + GUI.enabled = true; + + EditorGUILayout.Space(); + if (storage == null) storage = ftRenderLightmap.FindRenderSettingsStorage(); + var rmode = storage.renderSettingsUserRenderMode; + int sizeX = ftRenderLightmap.VolumeDimension(ftraceResX.intValue); + int sizeY = ftRenderLightmap.VolumeDimension(ftraceResY.intValue); + int sizeZ = ftRenderLightmap.VolumeDimension(ftraceResZ.intValue); + int vSize = 0; + if (storage.renderSettingsCompressVolumes) + { + const int blockDimension = 4; + const int blockByteSize = 16; // both BC6H and BC7 + int numBlocks = (sizeX/blockDimension) * (sizeY/blockDimension); + vSize = numBlocks * blockByteSize * sizeZ * 4; + } + else + { + vSize = sizeX*sizeY*sizeZ*8*3; + } + string note = "VRAM: " + FormatSize(vSize) + " MB " + (storage.renderSettingsCompressVolumes ? "(compressed color)" : "(color)"); + if (rmode == (int)ftRenderLightmap.RenderMode.Shadowmask || pstorage.volumeRenderMode == (int)BakeryLightmapGroup.RenderMode.Shadowmask) + { + note += ", " + FormatSize(sizeX*sizeY*sizeZ * (ftraceShadowmaskEncoding.intValue == 0 ? 4 : 1)) + " MB (mask)"; + } + EditorGUILayout.LabelField(note); + + //EditorGUILayout.PropertyField(ftraceAdjustSamples, new GUIContent("Adjust sample positions", "Fixes light leaking from inside surfaces")); + + EditorGUILayout.Space(); + + showExperimental = EditorGUILayout.Foldout(showExperimental, "Experimental", EditorStyles.foldout); + if (showExperimental) + { + EditorGUILayout.PropertyField(ftraceEncoding, new GUIContent("Encoding")); + EditorGUILayout.PropertyField(ftraceShadowmaskEncoding, new GUIContent("Shadowmask Encoding")); + + bool wasSet = ftraceRotation.boolValue; + EditorGUILayout.PropertyField(ftraceRotation, new GUIContent("Support rotation after bake", "Normally volumes can only be repositioned or rescaled at runtime. With this checkbox volume's rotation matrix will also be sent to shaders. Shaders must have a similar checkbox enabled.")); + if (wasSet != ftraceRotation.boolValue) + { + (target as BakeryVolume).SetGlobalParams(); + } + } + + EditorGUILayout.Space(); + + if (vol.bakedTexture0 == null) + { + EditorGUILayout.LabelField("Baked texture: none"); + } + else + { + EditorGUILayout.LabelField("Baked texture: " + vol.bakedTexture0.name); + } + + EditorGUILayout.Space(); + + var wrapObj = EditorGUILayout.ObjectField("Wrap to object", null, typeof(GameObject), true) as GameObject; + if (wrapObj != null) + { + var mrs = wrapObj.GetComponentsInChildren() as MeshRenderer[]; + if (mrs.Length > 0) + { + var b = mrs[0].bounds; + for(int i=1; i(); + if (boxCol != null) + { + if (GUILayout.Button("Set from box collider")) + { + Undo.RecordObject(vol, "Change Bounds"); + vol.bounds = boxCol.bounds; + } + if (GUILayout.Button("Set to box collider")) + { + boxCol.center = Vector3.zero; + boxCol.size = vol.bounds.size; + } + } + + var bmin = vol.bounds.min; + var bmax = vol.bounds.max; + var bsize = vol.bounds.size; + EditorGUILayout.LabelField("Min: " + bmin.x+", "+bmin.y+", "+bmin.z); + EditorGUILayout.LabelField("Max: " + bmax.x+", "+bmax.y+", "+bmax.z); + + if (GUILayout.Button("Copy bounds to clipboard")) + { + GUIUtility.systemCopyBuffer = "float3 bmin = float3(" + F(bmin.x)+", "+F(bmin.y)+", "+F(bmin.z) + "); float3 bmax = float3(" + F(bmax.x)+", "+F(bmax.y)+", "+F(bmax.z) + "); float3 binvsize = float3(" + F(1.0f/bsize.x)+", "+F(1.0f/bsize.y)+", "+F(1.0f/bsize.z) + ");"; + } + + serializedObject.ApplyModifiedProperties(); + } + + protected virtual void OnSceneGUI() + { + var vol = (BakeryVolume)target; + + boundsHandle.center = vol.transform.position; + boundsHandle.size = vol.bounds.size; + Handles.zTest = UnityEngine.Rendering.CompareFunction.Less; + + EditorGUI.BeginChangeCheck(); + boundsHandle.DrawHandle(); + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(vol, "Change Bounds"); + + Bounds newBounds = new Bounds(); + newBounds.center = boundsHandle.center; + newBounds.size = boundsHandle.size; + vol.bounds = newBounds; + vol.transform.position = boundsHandle.center; + } + else if ((vol.bounds.center - boundsHandle.center).sqrMagnitude > 0.0001f) + { + Bounds newBounds = new Bounds(); + newBounds.center = boundsHandle.center; + newBounds.size = boundsHandle.size; + vol.bounds = newBounds; + } + } +} +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftVolumeInspector.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftVolumeInspector.cs.meta new file mode 100644 index 00000000..f001d744 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftVolumeInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 96725c7b01d15304c92894c9b78b76d4 +timeCreated: 1608652614 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas.meta new file mode 100644 index 00000000..5cbbe7f0 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: cdeee0ee874e6f047a1e4aa6e2ba4692 +folderAsset: yes +timeCreated: 1622807249 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas-license.txt b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas-license.txt new file mode 100644 index 00000000..01d0b542 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas-license.txt @@ -0,0 +1,23 @@ +xatlas +https://github.com/jpcy/xatlas +Copyright (c) 2018 Jonathan Young + +thekla_atlas +https://github.com/Thekla/thekla_atlas +Copyright (c) 2013 Thekla, Inc +Copyright NVIDIA Corporation 2006 -- Ignacio Castano + +Fast-BVH +https://github.com/brandonpelfrey/Fast-BVH +Copyright (c) 2012 Brandon Pelfrey + +px_sched +https://github.com/pplux/px +Copyright (c) 2017-2018 Jose L. Hidalgo (PpluX) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +© 2019 GitHub, Inc. \ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas-license.txt.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas-license.txt.meta new file mode 100644 index 00000000..e3437ce5 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas-license.txt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 157fce07d9e165f4ea73f754793d6c48 +timeCreated: 1553351391 +licenseType: Store +TextScriptImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas.cs new file mode 100644 index 00000000..f27f9c32 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas.cs @@ -0,0 +1,289 @@ +using UnityEngine; +using UnityEditor; +using UnityEditor.SceneManagement; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +public class xatlas +{ + //#define UV_HINT + + public static List newUVBuffer; + public static List newXrefBuffer; + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern System.IntPtr xatlasCreateAtlas(); + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern int xatlasAddMesh(System.IntPtr atlas, int vertexCount, System.IntPtr positions, System.IntPtr normals, System.IntPtr uv, int indexCount, int[] indices32); + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern int xatlasAddUVMesh(System.IntPtr atlas, int vertexCount, System.IntPtr uv, int indexCount, int[] indices32, bool allowRotate); + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern void xatlasParametrize(System.IntPtr atlas); + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern void xatlasPack(System.IntPtr atlas, int attempts, float texelsPerUnit, int resolution, int maxChartSize, int padding, bool bruteForce, bool blockAlign);//, bool allowRotate); + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern void xatlasNormalize(System.IntPtr atlas, int[] atlasSizes); + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern int xatlasGetAtlasCount(System.IntPtr atlas); + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern int xatlasGetAtlasIndex(System.IntPtr atlas, int meshIndex, int chartIndex); + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern int xatlasGetVertexCount(System.IntPtr atlas, int meshIndex); + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern int xatlasGetIndexCount(System.IntPtr atlas, int meshIndex); + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern void xatlasGetData(System.IntPtr atlas, int meshIndex, System.IntPtr outUV, System.IntPtr outRef, System.IntPtr outIndices); + + [DllImport ("xatlasLib", CallingConvention=CallingConvention.Cdecl)] + public static extern int xatlasClear(System.IntPtr atlas); + + static T[] FillAtrribute(List xrefArray, T[] origArray) + { + if (origArray == null || origArray.Length == 0) return origArray; + + var arr = new T[xrefArray.Count]; + for(int i=0; i(); + //var newXref = new List(); + var indexBuffers = new List(); + + newUVBuffer = new List(); + newXrefBuffer = new List(); + while(newUVBuffer.Count < m.vertexCount) + { + newUVBuffer.Add(new Vector2(-100, -100)); + newXrefBuffer.Add(0); + } + + xatlasNormalize(atlas, null); + + // Collect UVs/xrefs/indices + for(int i=0; ifinal index mappings + var xatlasIndexToNewIndex = new int[newVertCount]; + for(int j=0; j= 65000;//0xFFFF; + if (is32bit && origIs16bit) + { + Debug.LogError("Unwrap failed: original mesh (" + m.name + ") has 16 bit indices, but unwrapped requires 32 bit."); + return; + } + + // Duplicate attributes + //if (newXrefBuffer.Count > m.vertexCount) // commented because can be also swapped around + { + m.vertices = FillAtrribute(newXrefBuffer, positions); + m.normals = FillAtrribute(newXrefBuffer, normals); + m.boneWeights = FillAtrribute(newXrefBuffer, m.boneWeights); + m.colors32 = FillAtrribute(newXrefBuffer, m.colors32); + m.tangents = FillAtrribute(newXrefBuffer, m.tangents); + m.uv = FillAtrribute(newXrefBuffer, m.uv); + m.uv3 = FillAtrribute(newXrefBuffer, m.uv3); + m.uv4 = FillAtrribute(newXrefBuffer, m.uv4); +#if UNITY_2018_2_OR_NEWER + m.uv5 = FillAtrribute(newXrefBuffer, m.uv5); + m.uv6 = FillAtrribute(newXrefBuffer, m.uv6); + m.uv7 = FillAtrribute(newXrefBuffer, m.uv7); + m.uv8 = FillAtrribute(newXrefBuffer, m.uv8); +#endif + } + + m.uv2 = newUVBuffer.ToArray(); + +/* + + // Set new UV2 + var finalUV2 = new Vector2[vertCount + newUV2.Count]; + for(int i=0; i