diff options
author | tylermurphy534 <tylermurphy534@gmail.com> | 2022-11-06 15:12:42 -0500 |
---|---|---|
committer | tylermurphy534 <tylermurphy534@gmail.com> | 2022-11-06 15:12:42 -0500 |
commit | eb84bb298d2b95aec7b2ae12cbf25ac64f25379a (patch) | |
tree | efd616a157df06ab661c6d56651853431ac6b08b /VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts | |
download | unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.gz unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.bz2 unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.zip |
move to self host
Diffstat (limited to 'VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts')
79 files changed, 28296 insertions, 0 deletions
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<GameObject> terrainObjectList; + static List<Terrain> terrainObjectToActual; + static List<Texture> terrainObjectToHeightMap; + static List<TexInput> terrainObjectToHeightMapRAM; + static List<float> terrainObjectToBounds; + static List<int> terrainObjectToLMID; + static List<float> terrainObjectToBoundsUV; + static List<int> terrainObjectToFlags; + static List<List<float[]>> terrainObjectToHeightMips; + //static List<List<Vector3[]>> terrainObjectToNormalMips; + //static List<Vector3[]> terrainObjectToNormalMip0; + static List<GameObject> temporaryAreaLightMeshList; + static List<BakeryLightMesh> temporaryAreaLightMeshList2; + public static List<GameObject> temporaryGameObjects; + + static Dictionary<GameObject, int> cmp_objToLodLevel; + static Dictionary<GameObject, float> cmp_holderObjArea; + + public static Dictionary<int,List<int>> lodLevelsVisibleInLodLevel = new Dictionary<int,List<int>>(); // defines LOD levels visible in chosen LOD level + + public static List<float> vbtraceTexPosNormalArray; // global vbTraceTex.bin positions/normals + public static List<float> vbtraceTexUVArray; // global vbTraceTex.bin UVs + public static float[] vbtraceTexUVArrayLOD; // global vbTraceTex.bin LOD UVs + + public static List<Renderer> atlasOnlyObj; + public static List<Vector4> atlasOnlyScaleOffset; + public static List<int> atlasOnlySize; + public static List<int> 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<vertices.Length;i++) + { + Vector3 pos = vertices[i];//t.(vertices[i]); + f.Write(pos.x); + f.Write(pos.y); + f.Write(pos.z); + } + } + + static void exportVBTrace(BufferedBinaryWriterFloat f, Mesh m, Vector3[] vertices, Vector3[] normals) + { + for(int i=0;i<vertices.Length;i++) + { + Vector3 pos = vertices[i];//t.(vertices[i]); + f.Write(pos.x); + f.Write(pos.y); + f.Write(pos.z); + + Vector3 normal = normals[i];//t.TransformDirection(normals[i]); + f.Write(normal.x); + f.Write(normal.y); + f.Write(normal.z); + } + } + + public static Renderer GetValidRenderer(GameObject obj) + { + var mr = obj.GetComponent<Renderer>(); + if (mr as MeshRenderer == null && mr as SkinnedMeshRenderer == null) + { + // possibly multiple renderers on one gameobject? + mr = obj.GetComponent<MeshRenderer>() as Renderer; + if (mr != null) return mr; + mr = obj.GetComponent<SkinnedMeshRenderer>() as Renderer; + if (mr != null) return mr; + return null; + } + return mr; + } + + static BakeryLightmapGroup GetLMGroupFromObjectExplicit(GameObject obj, ExportSceneData data) + { + lmgroupHolder = null; + var lmgroupSelector = obj.GetComponent<BakeryLightmapGroupSelector>(); // 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<BakeryLightmapGroupSelector>(); + t2 = t2.parent; + } + } + BakeryLightmapGroup lmgroup = null; + if (lmgroupSelector != null) + { + lmgroup = lmgroupSelector.lmgroupAsset as BakeryLightmapGroup; + lmgroupHolder = lmgroupSelector.gameObject; + + //var so = new SerializedObject(obj.GetComponent<Renderer>()); + //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<BakeryLightmapGroupSelector>(); // 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<BakeryLightmapGroupSelector>(); + 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<BakeryLightmapGroupSelector>(); // 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<BakeryLightmapGroupSelector>(); + 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<float> arrPosNormal, List<float> arrUV, + Vector3[] vertices, Vector3[] normals, Vector2[] uv2, int lmid, bool vertexBake, GameObject obj) + { + for(int i=0;i<vertices.Length;i++) + { + Vector3 pos = vertices[i];//t.(vertices[i]); + arrPosNormal.Add(pos.x); + arrPosNormal.Add(pos.y); + arrPosNormal.Add(pos.z); + + Vector3 normal = normals[i];//t.TransformDirection(normals[i]); + arrPosNormal.Add(normal.x); + arrPosNormal.Add(normal.y); + arrPosNormal.Add(normal.z); + + float u = 0; + float v = 0; + + if (lmid < 0) + { + //u = lmid * 10; + if (uv2.Length>0) + { + 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;i<vertCount;i++) + { + f.Write(0.0f); + f.Write(0.0f); + } + } + else + { + for(int i=0;i<vertCount;i++) + { + f.Write(uvs[i].x); + f.Write(uvs[i].y); + } + } + } + + static void exportVBBasic(BinaryWriter f, Transform t, Mesh m, Vector3[] vertices, Vector3[] normals, Vector2[] uv2) + { + for(int i=0;i<vertices.Length;i++) + { + Vector3 pos = vertices[i];//t.(vertices[i]); + f.Write(pos.x); + f.Write(pos.y); + f.Write(pos.z); + + Vector3 normal = normals[i];//t.TransformDirection(normals[i]); + f.Write(normal.x); + f.Write(normal.y); + f.Write(normal.z); + + if (uv2.Length>0) + { + 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<vertices.Length;i++) + { + Vector3 pos = vertices[i];//t.(vertices[i]); + f.Write(pos.x); + f.Write(pos.y); + f.Write(pos.z); + + Vector3 normal = normals[i];//t.TransformDirection(normals[i]); + f.Write(normal.x); + f.Write(normal.y); + f.Write(normal.z); + + if (tangents == null) + { + f.Write(0.0f); + f.Write(0.0f); + f.Write(0.0f); + f.Write(0.0f); + } + else + { + Vector4 tangent = tangents[i]; + f.Write(tangent.x); + f.Write(tangent.y); + f.Write(tangent.z); + f.Write(tangent.w); + } + + if (hasUV) + { + f.Write(uv[i].x); + f.Write(uv[i].y); + } + else + { + f.Write(0.0f); + f.Write(0.0f); + } + + if (hasUV2) + { + f.Write(uv2[i].x); + f.Write(uv2[i].y); + } + else + { + f.Write(0.0f); + f.Write(0.0f); + } + } + } + + static int exportIB(BufferedBinaryWriterInt f, int[] indices, bool isFlipped, bool is32Bit, int offset, BinaryWriter falphaid, ushort alphaID) + { + //var indices = m.GetTriangles(i); + for(int j=0;j<indices.Length;j+=3) + { + if (!isFlipped) + { + if (is32Bit) + { + f.Write(indices[j] + offset); + f.Write(indices[j+1] + offset); + f.Write(indices[j+2] + offset); + } + else + { + f.Write(indices[j]); + f.Write(indices[j+1]); + f.Write(indices[j+2]); + } + } + else + { + if (is32Bit) + { + f.Write(indices[j+2] + offset); + f.Write(indices[j+1] + offset); + f.Write(indices[j] + offset); + } + else + { + f.Write(indices[j+2]); + f.Write(indices[j+1]); + f.Write(indices[j]); + } + } + + if (falphaid!=null) falphaid.Write(alphaID); + } + return indices.Length; + } + + // returns mesh area if requested + static void exportIB32(List<int> indicesOpaque, List<int> indicesTransparent, List<int> 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<indices.Length;j+=3) + { + if (!isFlipped) + { + indexA = indices[j]; + indexB = indices[j + 1]; + indexC = indices[j + 2]; + } + else + { + indexA = indices[j + 2]; + indexB = indices[j + 1]; + indexC = indices[j]; + } + + indicesOut.Add(indexA + offset); + indicesOut.Add(indexB + offset); + indicesOut.Add(indexC + offset); + + if (indicesLMID != null) + { + indicesLMID.Add(indexA + indexOffsetLMID); + indicesLMID.Add(indexB + indexOffsetLMID); + indicesLMID.Add(indexC + indexOffsetLMID); + } + + if (alphaID != 0xFFFF) falphaid.Write(alphaID); + } + } + + + static void exportSurfs(BinaryWriter f, int[][] indices, int subMeshCount)// Mesh m) + { + int offset = ioffset; + for(int i=0;i<subMeshCount;i++) { + int size = indices[i].Length;//m.GetTriangles(i).Length; + f.Write(offset); + f.Write(size); + offset += size;// * 2; + } + soffset += subMeshCount; + } + + static void exportMesh(BinaryWriter f, Mesh m) + { + f.Write(voffset); + f.Write(soffset); + f.Write((ushort)m.subMeshCount); + f.Write((ushort)0); + } + + static int exportLMID(BinaryWriter f, GameObject obj, BakeryLightmapGroup lmgroup) + { + var areaLight = obj.GetComponent<BakeryLightMesh>(); + 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<vlength; i++) + { + int x = (i + voffset) % atlasTexSize; + int y = (i + voffset) / atlasTexSize; + uvs[i] = new Vector2(x * mul + add, y * mul + add);// - 1.0f); + } + return uvs; + } + + public static Mesh GetSharedMesh(Renderer mr) + { + if (mr == null) return null; + var mrSkin = mr as SkinnedMeshRenderer; + var mf = mr.gameObject.GetComponent<MeshFilter>(); + return mrSkin != null ? mrSkin.sharedMesh : (mf != null ? mf.sharedMesh : null); + } + + public static Mesh GetSharedMeshBaked(GameObject obj) + { + var mrSkin = obj.GetComponent<SkinnedMeshRenderer>(); + if (mrSkin != null) + { + var baked = new Mesh(); + mrSkin.BakeMesh(baked); + return baked; + } + var mf = obj.GetComponent<MeshFilter>(); + return (mf != null ? mf.sharedMesh : null); + } + + public static Mesh GetSharedMesh(GameObject obj) + { + var mrSkin = obj.GetComponent<SkinnedMeshRenderer>(); + var mf = obj.GetComponent<MeshFilter>(); + return mrSkin != null ? mrSkin.sharedMesh : (mf != null ? mf.sharedMesh : null); + } + + public static Mesh GetSharedMeshSkinned(GameObject obj, out bool isSkin) + { + var mrSkin = obj.GetComponent<SkinnedMeshRenderer>(); + 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<MeshFilter>(); + 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<BakeryPackAsSingleSquare>() != 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<storages.Length; i++) + { + var store = storages[i]; + if (store == null) continue; + var index = store.assetList.IndexOf(path); + if (index < 0) continue; + + if (store.uvOverlapAssetList[index] == 0) + { + return false; + } + else + { + return true; + } + }*/ + var index = store.assetList.IndexOf(path); + if (index >= 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<storages.Length; i++) + { + var store = storages[i]; + var index = store.assetList.IndexOf(path); + if (index < 0) continue; + + if (store.uvOverlapAssetList[index] == 0) + { + return false; + } + else + { + return true; + } + }*/ + index = store.assetList.IndexOf(path); + if (index >= 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<temporaryAreaLightMeshList.Count; i++) + { + if (temporaryAreaLightMeshList[i] != null) + { + //var mr = temporaryAreaLightMeshList[i].GetComponent<Renderer>(); + //if (mr != null) DestroyImmediate(mr); + //var mf = temporaryAreaLightMeshList[i].GetComponent<MeshFilter>(); + //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<terrainObjectList.Count; i++) + { + if (terrainObjectList[i] != null) DestroyImmediate(terrainObjectList[i]); + } + terrainObjectList = null; + } + + if (temporaryGameObjects != null) + { + for(int i=0; i<temporaryGameObjects.Count; i++) + { + if (temporaryGameObjects[i] != null) DestroyImmediate(temporaryGameObjects[i]); + } + temporaryGameObjects = null; + } + + //if (isError) + { + FreeTemporaryAreaLightMeshes(); + } + } + + if (ftRenderLightmap.showProgressBar) ftRenderLightmap.simpleProgressBarEnd(); + } + void OnInspectorUpdate() + { + Repaint(); + } + + static void CloseAllFiles() + { + if (fscene != null) fscene.Close(); + if (fmesh != null) fmesh.Close(); + if (flmid != null) flmid.Close(); + if (fseamfix != null) fseamfix.Close(); + if (fsurf != null) fsurf.Close(); + if (fmatid != null) fmatid.Close(); + if (fmatide != null) fmatide.Close(); + if (fmatideb != null) fmatideb.Close(); + if (fmatidh != null) fmatidh.Close(); + if (falphaid != null) falphaid.Close(); + if (fvbfull != null) fvbfull.Close(); + if (fvbtrace != null) fvbtrace.Close(); + if (fvbtraceTex != null) fvbtraceTex.Close(); + if (fvbtraceUV0 != null) fvbtraceUV0.Close(); + if (fib != null) fib.Close(); + if (fib32 != null) fib32.Close(); + if (fhmaps != null) fhmaps.Close(); + if (fib32lod != null) + { + for(int i=0; i<fib32lod.Length; i++) fib32lod[i].Close(); + } + if (falphaidlod != null) + { + for(int i=0; i<falphaidlod.Length; i++) falphaidlod[i].Close(); + } + fvbfull = fvbtrace = fvbtraceTex = fvbtraceUV0 = null; + fib = null; + fscene = fmesh = flmid = fsurf = fmatid = fmatide = fmatideb = falphaid = fib32 = fseamfix = fmatidh = fhmaps = null; + fib32lod = falphaidlod = null; + } + + static bool CheckUnwrapError() + { + if (ftModelPostProcessor.unwrapError) + { + + DebugLogError("Unity failed unwrapping some models. See console for details. Last failed model: " + ftModelPostProcessor.lastUnwrapErrorAsset+"\nModel will now be reverted to original state."); + + int mstoreIndex = gstorage.modifiedAssetPathList.IndexOf(ftModelPostProcessor.lastUnwrapErrorAsset); + if (mstoreIndex < 0) + { + Debug.LogError("Failed to find failed asset?"); + } + else + { + gstorage.ClearAssetModifications(mstoreIndex); + } + + ftModelPostProcessor.unwrapError = false; + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + return true; + } + return false; + } + + static Mesh BuildAreaLightMesh(Light areaLight) + { + var mesh = new Mesh(); + + var verts = new Vector3[4]; + + Vector2 areaSize = ftLightMeshInspector.GetAreaLightSize(areaLight); + + verts[0] = new Vector3(-0.5f * areaSize.x, -0.5f * areaSize.y, 0); + verts[1] = new Vector3(0.5f * areaSize.x, -0.5f * areaSize.y, 0); + verts[2] = new Vector3(0.5f * areaSize.x, 0.5f * areaSize.y, 0); + verts[3] = new Vector3(-0.5f * areaSize.x, 0.5f * areaSize.y, 0); + + var uvs = new Vector2[4]; + uvs[0] = new Vector2(0, 0); + uvs[1] = new Vector2(1, 0); + uvs[2] = new Vector2(1, 1); + uvs[3] = new Vector2(0, 1); + + var indices = new int[6]; + + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + + indices[3] = 0; + indices[4] = 2; + indices[5] = 3; + + var normals = new Vector3[4]; + var n = Vector3.forward;// -areaLight.transform.forward; // transformation will be applied later + for(int i=0; i<4; i++) normals[i] = n; + + mesh.vertices = verts; + mesh.triangles = indices; + mesh.normals = normals; + mesh.uv = uvs; + + return mesh; + } + + static bool CheckForTangentSHLights() + { + var All2 = FindObjectsOfType(typeof(BakerySkyLight)); + for(int i=0; i<All2.Length; i++) + { + var obj = All2[i] as BakerySkyLight; + if (!obj.enabled) continue; + if (!obj.gameObject.activeInHierarchy) continue; + if (obj.tangentSH) + { + return true; + } + } + return false; + } + + public static void CreateSceneFolder() + { + if (scenePath == "" || !Directory.Exists(scenePath)) + { + // Default scene path is TEMP/frender + var tempDir = System.Environment.GetEnvironmentVariable("TEMP", System.EnvironmentVariableTarget.Process); + scenePath = tempDir + "\\frender"; + if (!Directory.Exists(scenePath)) Directory.CreateDirectory(scenePath); + } + } + + static int CorrectLMGroupID(int id, BakeryLightmapGroup lmgroup, List<BakeryLightmapGroup> groupList) + { + id = id < 0 ? -1 : id; + if (lmgroup != null && lmgroup.parentName != null && lmgroup.parentName.Length > 0 && lmgroup.parentName != "|") + { + for(int g=0; g<groupList.Count; g++) + { + if (groupList[g].name == lmgroup.parentName) + { + id = g; + break; + } + } + } + return id; + } + + static void InitSceneStorage(ExportSceneData data) + { + var storages = data.storages; + var sceneToID = data.sceneToID; + + bool first = true; + for(int i=0; i<storages.Length; i++) + { + var scene = SceneManager.GetSceneAt(i); + if (!scene.isLoaded) continue; + var gg = ftLightmaps.FindInScene("!ftraceLightmaps", scene); + + if (gg == null) + { + gg = new GameObject(); + SceneManager.MoveGameObjectToScene(gg, scene); + gg.name = "!ftraceLightmaps"; + gg.hideFlags = HideFlags.HideInHierarchy; + } + + storages[i] = gg.GetComponent<ftLightmapsStorage>(); + if (storages[i] == null) + { + storages[i] = gg.AddComponent<ftLightmapsStorage>(); + } + + if (modifyLightmapStorage) + { + /* + storages[i].bakedRenderers = new List<Renderer>(); + storages[i].bakedIDs = new List<int>(); + storages[i].bakedScaleOffset = new List<Vector4>(); + storages[i].bakedVertexOffset = new List<int>(); + storages[i].bakedVertexColorMesh = new List<Mesh>(); + storages[i].bakedRenderersTerrain = new List<Terrain>(); + storages[i].bakedIDsTerrain = new List<int>(); + storages[i].bakedScaleOffsetTerrain = new List<Vector4>(); + */ + storages[i].hasEmissive = new List<bool>(); + storages[i].lmGroupLODResFlags = null; + storages[i].lmGroupMinLOD = null; + storages[i].lmGroupLODMatrix = null; + storages[i].nonBakedRenderers = new List<Renderer>(); + } + if (first) + { + data.firstNonNullStorage = i; + first = false; + } + storages[i].implicitGroups = new List<UnityEngine.Object>(); + storages[i].implicitGroupedObjects = new List<GameObject>(); + sceneToID[scene] = i; + } + + //var go = GameObject.Find("!ftraceLightmaps"); + //data.settingsStorage = go.GetComponent<ftLightmapsStorage>(); + } + + static void InitSceneStorage2(ExportSceneData data) + { + var storages = data.storages; + for(int i=0; i<storages.Length; i++) + { + var scene = SceneManager.GetSceneAt(i); + if (!scene.isLoaded) continue; + if (modifyLightmapStorage) + { + storages[i].bakedRenderers = new List<Renderer>(); + storages[i].bakedIDs = new List<int>(); + storages[i].bakedScaleOffset = new List<Vector4>(); + storages[i].bakedVertexOffset = new List<int>(); + storages[i].bakedVertexColorMesh = new List<Mesh>(); + storages[i].bakedRenderersTerrain = new List<Terrain>(); + storages[i].bakedIDsTerrain = new List<int>(); + storages[i].bakedScaleOffsetTerrain = new List<Vector4>(); + } + } + } + + // 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<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 posFile = new byte[128 + uvpos.Length * 4]; + System.Buffer.BlockCopy(ftDDS.ddsHeaderFloat4, 0, posFile, 0, 128); + System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posFile, 12, 4); + System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posFile, 16, 4); + System.Buffer.BlockCopy(uvpos, 0, posFile, 128, uvpos.Length * 4); + SaveGBufferMapFromRAM(posFile, posFile.Length, scenePath + "/uvpos_probes" + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), ftRenderLightmap.compressedGBuffer); + //GL.IssuePluginEvent(8); + //yield return null; + + var posNormal = new byte[128 + uvnormal.Length]; + System.Buffer.BlockCopy(ftDDS.ddsHeaderRGBA8, 0, posNormal, 0, 128); + System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posNormal, 12, 4); + System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posNormal, 16, 4); + System.Buffer.BlockCopy(uvnormal, 0, posNormal, 128, uvnormal.Length); + SaveGBufferMapFromRAM(posNormal, posNormal.Length, scenePath + "/uvnormal_probes" + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), ftRenderLightmap.compressedGBuffer); + //GL.IssuePluginEvent(8); + //yield return null; + + lightProbeLMGroup = ScriptableObject.CreateInstance<BakeryLightmapGroup>(); + 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<vols.Length; v++) + { + numTotalProbes += ftRenderLightmap.VolumeDimension(vols[v].resolutionX) * ftRenderLightmap.VolumeDimension(vols[v].resolutionY) * ftRenderLightmap.VolumeDimension(vols[v].resolutionZ); + } + + var positions = new Vector3[numTotalProbes]; + int i = 0; + Vector3 halfVoxelSize = Vector3.one; + for(int v=0; v<vols.Length; v++) + { + var vol = vols[v]; + int rx = ftRenderLightmap.VolumeDimension(vol.resolutionX); + int ry = ftRenderLightmap.VolumeDimension(vol.resolutionY); + int rz = ftRenderLightmap.VolumeDimension(vol.resolutionZ); + var bmin = vol.bounds.min; + var bmax = vol.bounds.max; + halfVoxelSize = bmax - bmin; + halfVoxelSize = new Vector3(halfVoxelSize.x/rx, halfVoxelSize.y/ry, halfVoxelSize.z/rz) * 0.5f; + float lx, ly, lz; + for(int z=0; z<rz; z++) + { + lz = Mathf.Lerp(bmin.z, bmax.z, z/(float)rz) + halfVoxelSize.z; + for(int y=0; y<ry; y++) + { + ly = Mathf.Lerp(bmin.y, bmax.y, y/(float)ry) + halfVoxelSize.y; + for(int x=0; x<rx; x++) + { + lx = Mathf.Lerp(bmin.x, bmax.x, x/(float)rx) + halfVoxelSize.x; + positions[i] = new Vector3(lx, ly, lz); + i++; + } + } + } + } + + int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)numTotalProbes)); + 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(i=0; i<numTotalProbes; 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 posFile = new byte[128 + uvpos.Length * 4]; + System.Buffer.BlockCopy(ftDDS.ddsHeaderFloat4, 0, posFile, 0, 128); + System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posFile, 12, 4); + System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posFile, 16, 4); + System.Buffer.BlockCopy(uvpos, 0, posFile, 128, uvpos.Length * 4); + SaveGBufferMapFromRAM(posFile, posFile.Length, scenePath + "/uvpos_volumes" + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), ftRenderLightmap.compressedGBuffer); + //GL.IssuePluginEvent(8); + //yield return null; + + var posNormal = new byte[128 + uvnormal.Length]; + System.Buffer.BlockCopy(ftDDS.ddsHeaderRGBA8, 0, posNormal, 0, 128); + System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posNormal, 12, 4); + System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posNormal, 16, 4); + System.Buffer.BlockCopy(uvnormal, 0, posNormal, 128, uvnormal.Length); + SaveGBufferMapFromRAM(posNormal, posNormal.Length, scenePath + "/uvnormal_volumes" + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), ftRenderLightmap.compressedGBuffer); + //GL.IssuePluginEvent(8); + //yield return null; + + volumeLMGroup = ScriptableObject.CreateInstance<BakeryLightmapGroup>(); + 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<BakeryLightmapGroupSelector>(FindObjectsOfType(typeof(BakeryLightmapGroupSelector)) as BakeryLightmapGroupSelector[]); + for(int i=0; i<groupSelectors.Count; i++) + { + var lmgroup = groupSelectors[i].lmgroupAsset as BakeryLightmapGroup; + if (lmgroup == null) continue; + if (!groupList.Contains(lmgroup)) + { + lmgroup.id = data.lmid; + lmgroup.sceneLodLevel = -1; + lmgroup.sceneName = ""; + groupList.Add(lmgroup); + lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0))); + data.lmid++; + } + EditorUtility.SetDirty(lmgroup); + } + } + + static bool CheckForMultipleSceneStorages(GameObject obj, ExportSceneData data) + { + var sceneHasStorage = data.sceneHasStorage; + + // Check for storage count in each scene + if (obj.name == "!ftraceLightmaps") + { + if (!sceneHasStorage.ContainsKey(obj.scene)) + { + sceneHasStorage[obj.scene] = true; + } + else + { + return ExportSceneValidationMessage("Scene " + obj.scene.name + " has multiple lightmap storage objects. This is not currently supported. Make sure you don't bake a scene with already baked prefabs."); + } + } + return true; + } + + static void ConvertTerrain(GameObject obj) + { + var terr = obj.GetComponent<Terrain>(); + 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<BakeryLightmapGroupSelector>(); + if (expGroup != null) + { + var expGroup2 = terrParent.AddComponent<BakeryLightmapGroupSelector>(); + 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<Camera>(); + 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<res; y++) + { + for(int x=0; x<res; x++) + { + height = heightmap[x,y]; + if (height > maxHeight) maxHeight = height; + } + } + maxHeight = Mathf.Max(maxHeight, 0.0001f); + float invMaxHeight = 1.0f / maxHeight; + for(int y=0; y<res; y++) + { + for(int x=0; x<res; x++) + { + heightmap[x,y] *= invMaxHeight; + } + } + float aabbHeight = maxHeight * scaleY; + + // First mip is the real heightmap + System.Buffer.BlockCopy(heightmap, 0, bytes, 0, bytes.Length); + hmap.Write(bytes); + + if (isDX11) + { + var htex = new Texture2D(res, res, TextureFormat.RFloat, false, true); + htex.LoadRawTextureData(bytes); + htex.Apply(); + terrainObjectToHeightMap.Add(htex); + } + else + { + var a = new TexInput(); + a.data = bytes; + a.width = (ushort)res; + a.height = a.width; + terrainObjectToHeightMapRAM.Add(a); + terrainObjectToHeightMap.Add(null); + } + + // min + terrainObjectToBounds.Add(obj.transform.position.x); + terrainObjectToBounds.Add(obj.transform.position.y); + terrainObjectToBounds.Add(obj.transform.position.z); + // max + terrainObjectToBounds.Add(obj.transform.position.x + tdata.size.x); + terrainObjectToBounds.Add(obj.transform.position.y + aabbHeight); + terrainObjectToBounds.Add(obj.transform.position.z + tdata.size.z); + + // filled later + terrainObjectToLMID.Add(0); + terrainObjectToBoundsUV.Add(0); + terrainObjectToBoundsUV.Add(0); + terrainObjectToBoundsUV.Add(0); + terrainObjectToBoundsUV.Add(0); + + terrainObjectToFlags.Add(terr.castShadows ? 1 : 0); + + // Second mip is max() of 3x3 corners + float[] floats = null; + float[] floatsPrev = null; + float[] floatsTmp; + int mipCount = 1; + int mipRes = res / 2; + //int prevRes = res; + float h00, h10, h01, h11; + /*Vector3 n00, n10, n01, n11; + Vector3[] normals = null; + Vector3[] normalsPrev = null; + + var origNormals = new Vector3[res * res]; + for(int y=0; y<res; y++) + { + for(int x=0; x<res; x++) + { + origNormals[y*res+x] = data.GetInterpolatedNormal(x / (float)res, y / (float)res); + } + }*/ + int terrIndex = terrainObjectToHeightMap.Count - 1;//terrainObjectToNormalMip0.Count; + //terrainObjectToNormalMip0.Add(origNormals); + //normalsPrev = origNormals; + terrainObjectToHeightMips.Add(new List<float[]>()); + //terrainObjectToNormalMips.Add(new List<Vector3[]>()); + + if (mipRes > 0) + { + floats = new float[mipRes * mipRes]; + //normals = new Vector3[mipRes * mipRes]; + for(int y=0; y<mipRes; y++) + { + for(int x=0; x<mipRes; x++) + { + /*h00 = heightmap[y*2,x*2]; + h10 = heightmap[y*2,x*2+1]; + h01 = heightmap[y*2+1,x*2]; + h11 = heightmap[y*2+1,x*2+1]; + height = h00 > 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<mipRes; y++) + { + for(int x=0; x<mipRes; x++) + { + h00 = floatsPrev[y*2 * mipRes*2 + x*2]; + h10 = floatsPrev[y*2 * mipRes*2 + x*2+1]; + h01 = floatsPrev[(y*2+1) * mipRes*2 + x*2]; + h11 = floatsPrev[(y*2+1) * mipRes*2 + x*2+1]; + height = h00 > 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<MeshFilter>(); + var mr = terrGO.AddComponent<MeshRenderer>(); + 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<numPatches; patchX++) + { + for (int patchY=0; patchY<numPatches; patchY++) + { + int patchResX = patchX < numPatches-1 ? patchRes : (res - patchRes*(numPatches-1)); + int patchResY = patchY < numPatches-1 ? patchRes : (res - patchRes*(numPatches-1)); + if (patchX < numPatches-1) patchResX += 1; + if (patchY < numPatches-1) patchResY += 1; + numVerts = patchResX * patchResY; + var posOffset = gposOffset + new Vector3(patchX*patchRes*scaleX, 0, patchY*patchRes*scaleZ); + + var positions = new Vector3[numVerts]; + var uvs = new Vector2[numVerts]; + var normals = new Vector3[numVerts]; + var indices = new int[(patchResX-1) * (patchResY-1) * 2 * 3]; + int vertOffset = 0; + int indexOffset = 0; + + for (int x=0;x<patchResX;x++) + { + for (int y=0;y<patchResY;y++) + { + int gx = x + patchX * patchRes; + int gy = y + patchY * patchRes; + + int index = x * patchResY + y; + float height = heightmap[gy,gx]; + + positions[index] = new Vector3(x * scaleX, height * scaleY, y * scaleZ) + posOffset; + uvs[index] = new Vector2(gx * uvscale.x + uvoffset.x, gy * uvscale.y + uvoffset.y); + + normals[index] = tdata.GetInterpolatedNormal(gx / (float)res, gy / (float)res); + + if (x < patchResX-1 && y < patchResY-1) + { + indices[indexOffset] = vertOffset + patchResY + 1; + indices[indexOffset + 1] = vertOffset + patchResY; + indices[indexOffset + 2] = vertOffset; + + indices[indexOffset + 3] = vertOffset + 1; + indices[indexOffset + 4] = vertOffset + patchResY + 1; + indices[indexOffset + 5] = vertOffset; + + indexOffset += 6; + } + + vertOffset++; + } + } + + var mesh = new Mesh(); + mesh.vertices = positions; + mesh.triangles = indices; + mesh.normals = normals; + mesh.uv = uvs; + mesh.uv2 = uvs; + + var terrGO = new GameObject(); + terrGO.name = "__ExportTerrain"; + GameObjectUtility.SetStaticEditorFlags(terrGO, StaticEditorFlags.LightmapStatic); + terrGO.transform.parent = terrParent.transform; + var mf = terrGO.AddComponent<MeshFilter>(); + var mr = terrGO.AddComponent<MeshRenderer>(); + 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<LODGroup>(); + if (lodGroup == null) + { + var renderers = newObj.GetComponentsInChildren<Renderer>(); + for(int r=0; r<renderers.Length; r++) + { + GameObjectUtility.SetStaticEditorFlags(renderers[r].gameObject, StaticEditorFlags.LightmapStatic); + var s = new SerializedObject(renderers[r]); + s.FindProperty("m_ScaleInLightmap").floatValue = 0; + s.ApplyModifiedProperties(); + } + } + else + { + var lods = lodGroup.GetLODs(); + for (int tl = 0; tl < lods.Length; tl++) + { + for (int h = 0; h < lods[tl].renderers.Length; h++) + { + GameObjectUtility.SetStaticEditorFlags(lods[tl].renderers[h].gameObject, tl==0 ? StaticEditorFlags.LightmapStatic : 0); + if (tl == 0) + { + var s = new SerializedObject(lods[tl].renderers[h]); + s.FindProperty("m_ScaleInLightmap").floatValue = 0; + s.ApplyModifiedProperties(); + } + } + } + } + + var xform = newObj.transform; + xform.localScale = new Vector3(trees[t].widthScale, trees[t].heightScale, trees[t].widthScale); + } + } + } + + static bool ConvertUnityAreaLight(GameObject obj) + { + // Add temporary meshes to area lights + var areaLightMesh = obj.GetComponent<BakeryLightMesh>(); + if (areaLightMesh != null) + { + var areaLight = obj.GetComponent<Light>(); + var mr = GetValidRenderer(obj); + var mf = obj.GetComponent<MeshFilter>(); + + 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<MeshFilter>(); + mf.sharedMesh = BuildAreaLightMesh(areaLight); + mr = areaObj.AddComponent<MeshRenderer>(); + + 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<int, List<int>>(); + + const int maxSceneLodLevels = 100; + var sceneLodUsed = new int[maxSceneLodLevels]; + for(int i=0; i<maxSceneLodLevels; i++) sceneLodUsed[i] = -1; + var lodGroups = Resources.FindObjectsOfTypeAll(typeof(LODGroup)); + var lodLevelsInLodGroup = new List<int>[lodGroups.Length]; + var localLodLevelsInLodGroup = new List<int>[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<lods.Length; i++) + { + var lodRenderers = lods[i].renderers; + if (lodRenderers.Length == 0) continue; + + bool lightmappedLOD = false; + for(int j=0; j<lodRenderers.Length; j++) + { + var r = lodRenderers[j]; + if (r == null) continue; + if (!r.enabled) continue; + if (!r.gameObject.activeInHierarchy) continue; + if ((r.gameObject.hideFlags & (HideFlags.DontSave|HideFlags.HideAndDontSave)) != 0) continue; // skip temp objects + if (r.gameObject.tag == "EditorOnly") continue; // skip temp objects + if ((GameObjectUtility.GetStaticEditorFlags(r.gameObject) & StaticEditorFlags.LightmapStatic) == 0) continue; // skip dynamic + var mr = GetValidRenderer(r.gameObject); + var sharedMesh = GetSharedMesh(mr); + if (mr == null || sharedMesh == null) continue; // must have visible mesh + var mrEnabled = mr.enabled || r.gameObject.GetComponent<BakeryAlwaysRender>() != 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<int>(); + localLodLevelsInLodGroup[lcounter] = new List<int>(); + } + if (lodLevelsInLodGroup[lcounter].IndexOf(newLodLevel) < 0) + { + lodLevelsInLodGroup[lcounter].Add(newLodLevel); + localLodLevelsInLodGroup[lcounter].Add(i); + } + + for(int j=0; j<lodRenderers.Length; j++) + { + var r = lodRenderers[j]; + if (r == null) continue; + int existingLodLevel = -1; + if (objToLodLevel.ContainsKey(r.gameObject)) existingLodLevel = objToLodLevel[r.gameObject]; + if (existingLodLevel < newLodLevel) + { + objToLodLevel[r.gameObject] = existingLodLevel < 0 ? newLodLevel : existingLodLevel; // set to lowest LOD + + // Collect LOD levels where this object is visible + List<int> visList; + if (!objToLodLevelVisible.TryGetValue(r.gameObject, out visList)) objToLodLevelVisible[r.gameObject] = visList = new List<int>(); + visList.Add(newLodLevel); + } + } + } + } + + // Sort scene LOD levels + int counter = 0; + var unsortedLodToSortedLod = new int[maxSceneLodLevels]; + for(int i=0; i<maxSceneLodLevels; i++) + { + int unsorted = sceneLodUsed[i]; + if (unsorted >= 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<visList.Count; j++) + { + visList[j] = unsortedLodToSortedLod[visList[j]]; + } + } + for(int i=0; i<lodLevelsInLodGroup.Length; i++) + { + if (lodLevelsInLodGroup[i] == null) continue; + var levels = lodLevelsInLodGroup[i]; + for(int j=0; j<levels.Count; j++) + { + levels[j] = unsortedLodToSortedLod[levels[j]]; + } + } + + // Fill LOD gaps + for(int i=0; i<lodLevelsInLodGroup.Length; i++) + { + if (lodLevelsInLodGroup[i] == null) continue; + var levels = lodLevelsInLodGroup[i]; + var localLevels = localLodLevelsInLodGroup[i]; + var lgroup = lodGroups[i] as LODGroup; + var lods = lgroup.GetLODs(); + for(int j=0; j<levels.Count; j++) + { + int level = levels[j]; + int localLevel = localLevels[j]; + int nextLevel = (j == levels.Count-1) ? (sceneLodsUsed-1) : levels[j+1]; + if (nextLevel - level > 1) + { + var lodRenderers = lods[localLevel].renderers; + for(int k=0; k<lodRenderers.Length; k++) + { + var r = lodRenderers[k]; + if (r == null) continue; + + var visList = objToLodLevelVisible[r.gameObject]; + for(int l=level+1; l<nextLevel; l++) + { + visList.Add(l); + } + } + } + } + } + + // Compute which LOD levels are visible in other LOD levels + foreach(var objToVisible in objToLodLevelVisible) + { + var obj = objToVisible.Key; + var objAffects = objToVisible.Value; + if (objAffects == null) continue; + + int objOwnLOD = -1; + if (!objToLodLevel.TryGetValue(obj, out objOwnLOD)) continue; + if (objOwnLOD < 0) continue; + + for(int i=0; i<objAffects.Count; i++) + { + int affectedLOD = objAffects[i]; + + List<int> visList; + if (!lodLevelsVisibleInLodLevel.TryGetValue(affectedLOD, out visList)) lodLevelsVisibleInLodLevel[affectedLOD] = visList = new List<int>(); + if (visList.IndexOf(objOwnLOD) < 0) visList.Add(objOwnLOD); + } + } + /*foreach(var pair in lodLevelsVisibleInLodLevel) + { + string str = "LOD " + pair.Key + " sees: "; + for(int i=0; i<pair.Value.Count; i++) + { + str += pair.Value[i] + ", "; + } + Debug.LogError(str); + }*/ + + DebugLogInfo("Scene LOD levels: " + sceneLodsUsed); + + // Init scene LOD index buffers + data.indicesOpaqueLOD = new List<int>[sceneLodsUsed]; + data.indicesTransparentLOD = new List<int>[sceneLodsUsed]; + for(int i=0; i<sceneLodsUsed; i++) + { + data.indicesOpaqueLOD[i] = new List<int>(); + data.indicesTransparentLOD[i] = new List<int>(); + } + + // 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<Renderer> 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<Camera>(); + 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<sector.tforms.Count; i++) + { + cullMatrices[i] = Matrix4x4.TRS(sector.tforms[i].position, sector.tforms[i].rotation, sector.tforms[i].localScale).inverse;// + Vector3.one * sector.nearDistance).inverse;//sector.tforms[i].worldToLocalMatrix; + //cullMatrices[i] = sector.tforms[i].worldToLocalMatrix; + } + Shader.SetGlobalMatrixArray("cullMatrices", cullMatrices); + Shader.SetGlobalFloat("cullMatricesCount", cullMatrices.Length); + }*/ + + // Iterate over cube faces + for(int i=0; i<6; i++) + { + // Render cube face + + // Render far objects + var mrt = new RenderBuffer[2]; + mrt[0] = outData.albedo[i].colorBuffer; + mrt[1] = outData.normal[i].colorBuffer; + Graphics.SetRenderTarget(mrt, outData.depth[i].depthBuffer); + GL.Clear(true, true, new Color(0,0,0, 1.0f / 255.0f)); // 0 is reserverd for projClip + + // Get view matrix for this cube face + Matrix4x4 v; + if (useCamera) + { + camTform.localEulerAngles = rot[i]; + v = tempCam.worldToCameraMatrix; + } + else + { + v = Matrix4x4.TRS(capturePoint, Quaternion.Euler(rot[i]), new Vector3(1,1,-1)).inverse; + } + // Compute/set viewProj + var vp = uProj * v; + if (Camera.current != null) vp *= Camera.current.worldToCameraMatrix.inverse; + GL.LoadProjectionMatrix(vp); + outData.viewProj[i] = proj * v; + + // Render near objects to depth + Graphics.SetRenderTarget(mrt, outData.depth[i].depthBuffer); + farSphereMatOcc.SetPass(0); + for(int o=0; o<objCount; o++) + { + var m = objsToWrite[o].GetComponent<MeshFilter>().sharedMesh; + var worldMatrix = objsToWrite[o].transform.localToWorldMatrix; + for(int s=0; s<m.subMeshCount; s++) + { + Graphics.DrawMeshNow(m, worldMatrix, s); + } + } + + var uvOffset = ftUVGBufferGen.uvOffset; + float texelSize = 1.0f / cubeRes; + var pTo = Shader.PropertyToID("texelOffset"); + + if (useCamera) + { + // Render far objects via camera + tempCam.SetTargetBuffers(mrt, outData.depth[i].depthBuffer); + tempCam.Render(); + } + else + { + // ... or via individual draws (SRP) + Graphics.SetRenderTarget(mrt, outData.depth[i].depthBuffer); + GL.sRGBWrite = true; + GL.LoadProjectionMatrix(vp); + for(int o=0; o<outsideRenderers.Count; o++) + { + var m = outsideRenderers[o].GetComponent<MeshFilter>().sharedMesh; + var worldMatrix = outsideRenderers[o].transform.localToWorldMatrix; + var mats = outsideRenderers[o].sharedMaterials; + for(int s=0; s<m.subMeshCount; s++) + { + int pass = 0; + if (mats.Length > 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<uvOffset.Length/2; j++) + { + var to = new Vector4(uvOffset[j*2] * texelSize, uvOffset[j*2+1] * texelSize, 0, 0); + farSphereMat.SetVector(pTo, to); + farSphereMat.SetPass(pass); + Graphics.DrawMeshNow(m, worldMatrix, s); + } + } + } + } + + } + + if (useCamera) + { + DestroyImmediate(tempCamGO); + } + + return outData; + } + + static bool GenerateFarSpheres(ExportSceneData data, FarSphereRenderData[] fdatas, List<Transform> 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<vertWidth-1; y++) + { + for(int x=0; x<vertWidth-1; x++) + { + tris[indCount] = y*vertWidth+x; + tris[indCount+1] = y*vertWidth+x+1; + tris[indCount+2] = (y+1)*vertWidth+x; + indCount += 3; + tris[indCount] = y*vertWidth+x+1; + tris[indCount+1] = (y+1)*vertWidth+x+1; + tris[indCount+2] = (y+1)*vertWidth+x; + indCount += 3; + } + } + + // Create shared UVs + var uv = new Vector2[vertWidth * vertWidth]; + float invDiv = 1.0f / (vertWidth-1); + for(int y=0; y<vertWidth; y++) + { + int yoffset = y*vertWidth; + for(int x=0; x<vertWidth; x++) + { + uv[yoffset+x] = new Vector2(x*invDiv, 1.0f - y*invDiv); + } + } + + ftLightmapsStorage sceneST = null; + int totalTris = 0; + + var tempTex = new RenderTexture(cubeRes, cubeRes, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); + tempTex.Create(); + var cmd = new CommandBuffer(); + var rtiTemp = new RenderTargetIdentifier(tempTex); + var mblock = new MaterialPropertyBlock(); + if (farSphereDilateShader == null) + { + farSphereDilateShader = Shader.Find("Hidden/ftFarSphereDilate"); + if (farSphereDilateShader == null) + { + Debug.LogError("Can't find farSphereDilate shader"); + return false; + } + } + if (farSphereDilateMat == null) + { + farSphereDilateMat = new Material(farSphereDilateShader); + } + var pMainTex = Shader.PropertyToID("_MainTex"); + + for(int f=0; f<fdatas.Length; f++) + { + var fdata = fdatas[f]; + fdata.meshes = new Mesh[6]; + fdata.textures = new Texture2D[6]; + var capturePoint = capturePoints[f].position; + + // Iterate over cube faces + for(int i=0; i<6; i++) + { + // Create tex readable by the lightmapper + var texAlbedo = new Texture2D(cubeRes, cubeRes, TextureFormat.RGBA32, false, false); + texAlbedo.wrapMode = TextureWrapMode.Clamp; + texAlbedo.filterMode = FilterMode.Point; + fdata.textures[i] = texAlbedo; + + // Dilate + mblock.SetTexture(pMainTex, fdata.albedo[i]); + cmd.Clear(); + cmd.SetRenderTarget(rtiTemp, 0, CubemapFace.Unknown, 0); + cmd.DrawProcedural(Matrix4x4.identity, farSphereDilateMat, 0, MeshTopology.Triangles, 6, 1, mblock); + Graphics.ExecuteCommandBuffer(cmd); + + // Copy tex + //Graphics.SetRenderTarget(fdata.albedo[i]); + Graphics.SetRenderTarget(tempTex); + texAlbedo.ReadPixels(new Rect(0,0,cubeRes,cubeRes), 0, 0, false); + texAlbedo.Apply(); + Graphics.SetRenderTarget(null); + + // Create verts + var verts = new Vector3[vertWidth * vertWidth]; + for(int y=0; y<vertWidth; y++) + { + int yoffset = y*vertWidth; + float fy = -0.5f + y * invDiv; + for(int x=0; x<vertWidth; x++) + { + float fx = -0.5f + x * invDiv; + uv[yoffset+x] = new Vector2(x*invDiv, 1.0f - y*invDiv); + switch(i) + { + case 0: + verts[yoffset+x] = new Vector3(-0.5f, -fy, fx); + break; + case 1: + verts[yoffset+x] = new Vector3(0.5f, -fy, -fx); + break; + case 2: + verts[yoffset+x] = new Vector3(fx, -0.5f, -fy); + break; + case 3: + verts[yoffset+x] = new Vector3(fx, 0.5f, fy); + break; + case 4: + verts[yoffset+x] = new Vector3(fx, fy, -0.5f); + break; + case 5: + verts[yoffset+x] = new Vector3(-fx, fy, 0.5f); + break; + } + } + } + + // Displace verts + rwBuff.SetData(verts); + farSphereCSTransform.SetBuffer(0, "verts", rwBuff); + farSphereCSTransform.SetTexture(0, "_DepthTex", fdata.depth[i]); + farSphereCSTransform.SetVector("objectCenter", capturePoint); + farSphereCSTransform.SetInt("vertWidth", vertWidth); + var ivp = fdata.viewProj[i].inverse; + farSphereCSTransform.SetVector("_InvProj0", ivp.GetRow(0)); + farSphereCSTransform.SetVector("_InvProj1", ivp.GetRow(1)); + farSphereCSTransform.SetVector("_InvProj2", ivp.GetRow(2)); + farSphereCSTransform.SetVector("_InvProj3", ivp.GetRow(3)); + //farSphereCSTransform.SetVector("wnormal", wnormal[i]); + farSphereCSTransform.Dispatch(0, dispatchWidth, dispatchWidth, 1); + rwBuff.GetData(verts); + + // Cull tris + indBuff.SetData(tris); + appendBuff.SetCounterValue(0); + uvBuff.SetData(uv); + farSphereCSCull.SetBuffer(0, "verts", rwBuff); + farSphereCSCull.SetBuffer(0, "uvs", uvBuff); + farSphereCSCull.SetBuffer(0, "indices", indBuff); + farSphereCSCull.SetBuffer(0, "newIndices", appendBuff); + farSphereCSCull.SetInt("triCount", tris.Length/3); + farSphereCSCull.SetTexture(0, "alphaTex", texAlbedo); + farSphereCSCull.SetFloat("cubeSize", ftAdditionalConfig.sectorFarSphereResolution); + farSphereCSCull.Dispatch(0, dispatchIndexGroups, 1, 1); + + // Get culled count + ComputeBuffer.CopyCount(appendBuff, countBuff, 0); + countBuff.GetData(countArray); + + // Get new tris + var arr = new int[countArray[0]*3]; + appendBuff.GetData(arr); + + // Create temp material + var mat = new Material(farSphereSshader); + mat.mainTexture = texAlbedo; + + // Create temp object + var faceMesh = new GameObject(); + faceMesh.name = "f_" + f + "_" + i; + faceMesh.transform.position = capturePoint; + temporaryGameObjects.Add(faceMesh); + + var mr = faceMesh.AddComponent<MeshRenderer>(); + 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<MeshFilter>(); + + 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<capture.meshes.Count; i++) + { + var capturePoint = capture.positions[i]; + + // Create temp material + var mat = new Material(farSphereSshader); + mat.mainTexture = capture.textures[i]; + + // Create temp object + var faceMesh = new GameObject(); + faceMesh.name = "f_" + i; + var faceTform = faceMesh.transform; + faceTform.position = capturePoint; + faceTform.parent = parentTform; + temporaryGameObjects.Add(faceMesh); + + var mr = faceMesh.AddComponent<MeshRenderer>(); + 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<MeshFilter>(); + 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<BakeryLightmapGroup>(); + 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<Renderer> 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<BakeryLightMesh>(); + 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<BakeryAlwaysRender>() != null; + if (!mrEnabled && areaLight == null) continue; + + var so = new SerializedObject(mr);//obj.GetComponent<Renderer>()); + 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<usedUVs.Length; v++) + { + if (usedUVs[v].x < -0.0001f || usedUVs[v].x > 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<inds.Length; n++) inds[n] = sharedMesh.GetTriangles(n); + objsToWriteIndices.Add(inds); + + if (group != null) group.passedFilter = ftRenderLightmap.passedFilterFlag; + } + + return true; + } + + static void CalculateVertexCountForVertexGroups(ExportSceneData data) + { + var objsToWrite = data.objsToWrite; + var objsToWriteGroup = data.objsToWriteGroup; + + // Calculate total vertex count for vertex-baked groups + for(int i=0; i<objsToWrite.Count; i++) + { + var lmgroup = objsToWriteGroup[i]; + if (lmgroup == null || lmgroup.mode != BakeryLightmapGroup.ftLMGroupMode.Vertex) continue; + var sharedMesh = GetSharedMesh(objsToWrite[i]); + lmgroup.totalVertexCount += sharedMesh.vertexCount; + } + } + + static void CreateAutoAtlasLMGroups(ExportSceneData data, bool renderTextures, bool atlasOnly) + { + var objsToWrite = data.objsToWrite; + var objsToWriteLightmapped = data.objsToWriteLightmapped; + var objsToWriteGroup = data.objsToWriteGroup; + var objsToWriteHolder = data.objsToWriteHolder; + var objToLodLevel = data.objToLodLevel; + var storages = data.storages; + var sceneToID = data.sceneToID; + var groupList = data.groupList; + var lmBounds = data.lmBounds; + var autoAtlasGroups = data.autoAtlasGroups; + var autoAtlasGroupRootNodes = data.autoAtlasGroupRootNodes; + + // Create implicit temp LMGroups. + // If object is a part of prefab, and if UVs are not generated in Unity, group is only addded to the topmost object (aka holder). + + // Implicit groups are added on every static object without ftLMGroupSelector. + // (Also init lmBounds and LMID as well) + // if autoAtlas == false: new group for every holder. + // if autoAtlas == true: single group for all holders (will be split later). + for(int i=0; i<objsToWrite.Count; i++) + { + if (!objsToWriteLightmapped[i]) continue; // skip objects with scaleInLM == 0 + if (objsToWriteGroup[i] != null) continue; // skip if already has lightmap assigned + var obj = objsToWrite[i]; + + var holder = obj; // holder is object itself (packed individually) + holder = TestPackAsSingleSquare(holder); + var prefabParent = PrefabUtility.GetPrefabParent(obj) as GameObject; + if (prefabParent != null) // object is part of prefab + { + // unity doesn't generate non-overlapping UVs for the whole model, only submeshes + // // if importer == null, it's an actual prefab, not model <-- not really; importer points to mesh's prefab, not real + // importer of a mesh is always model asset + // importers of components never exist + // at least check the prefab type + var ptype = PrefabUtility.GetPrefabType(prefabParent); + if (ptype == PrefabType.ModelPrefab) + { + var sharedMesh = GetSharedMesh(obj); + var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(sharedMesh)) as ModelImporter; + + if (importer != null && !ModelUVsOverlap(importer, gstorage)) + { + // 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; + var assetG = PrefabUtility.GetPrefabParent(g) as GameObject; + while(assetG != assetTopMost && g.transform.parent != null && assetG.transform.parent != null) + { + var g2 = g.transform.parent.gameObject; + var assetG2 = assetG.transform.parent.gameObject; + + if (PrefabUtility.GetPrefabParent(g2) != assetG2) break; // avoid using parents which don't belong to this model + + g = g2; + assetG = assetG2; + } + var sceneTopMost = g; + holder = sceneTopMost; // holder is topmost model object (non-overlapped UVs) + + int lodLevel; + if (objToLodLevel.TryGetValue(obj, out lodLevel)) holder = obj; // separated if used in LOD + } + } + } + else if (obj.name == "__ExportTerrain") + { + holder = obj.transform.parent.gameObject; // holder is terrain parent + + int lodLevel; + if (objToLodLevel.TryGetValue(obj, out lodLevel)) holder = obj; // separated if used in LOD + } + + if (!storages[sceneToID[holder.scene]].implicitGroupedObjects.Contains(holder)) + { + BakeryLightmapGroup newGroup; + if (autoAtlas && autoAtlasGroups.Count > 0) + { + newGroup = autoAtlasGroups[0]; + } + else + { + newGroup = ScriptableObject.CreateInstance<BakeryLightmapGroup>(); + + // 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<gholders.Count; g++) + { + if (gholders[g] == holder) + { + tempStorage.implicitGroupMap[holder] = grs[g]; + break; + } + } + } + + objsToWriteGroup[i] = (BakeryLightmapGroup)tempStorage.implicitGroupMap[holder]; + objsToWriteHolder[i] = holder; + } + } + + static void TransformVertices(ExportSceneData data, bool tangentSHLights, int onlyID = -1) + { + var objsToWrite = data.objsToWrite; + var objsToWriteGroup = data.objsToWriteGroup; + var objsToWriteVerticesPosW = data.objsToWriteVerticesPosW; + var objsToWriteVerticesNormalW = data.objsToWriteVerticesNormalW; + var objsToWriteVerticesTangentW = data.objsToWriteVerticesTangentW; + + int startIndex = 0; + int endIndex = objsToWrite.Count-1; + + if (onlyID >= 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<vertices.Length; t++) + { + vertices[t].Scale(inverseWorldScale); + } + } + for(int t=0; t<vertices.Length; t++) + { + objsToWriteVerticesPosW[i][t] = tform.TransformPoint(vertices[t]); + } + var normals = m.normals; + objsToWriteVerticesNormalW[i] = new Vector3[vertices.Length]; + var nbuff = objsToWriteVerticesNormalW[i]; + var localScale = obj.transform.localScale; + bool flipX = localScale.x < 0; + bool flipY = localScale.y < 0; + bool flipZ = localScale.z < 0; + if (lmgroup != null && lmgroup.flipNormal) + { + flipX = !flipX; + flipY = !flipY; + flipZ = !flipZ; + } + for(int t=0; t<vertices.Length; t++) + { + if (normals.Length == 0) + { + nbuff[t] = Vector3.up; + } + else + { + nbuff[t] = normals[t]; + if (flipX) nbuff[t].x *= -1; + if (flipY) nbuff[t].y *= -1; + if (flipZ) nbuff[t].z *= -1; + nbuff[t] = tform.TransformDirection(nbuff[t]); + } + } + if (NeedsTangents(lmgroup, tangentSHLights)) + { + var tangents = m.tangents; + while(objsToWriteVerticesTangentW.Count <= i) objsToWriteVerticesTangentW.Add(null); + objsToWriteVerticesTangentW[i] = new Vector4[vertices.Length]; + var tbuff = objsToWriteVerticesTangentW[i]; + Vector3 tangent = Vector3.zero; + for(int t=0; t<vertices.Length; t++) + { + if (tangents.Length == 0) + { + tbuff[t] = Vector3.right; + } + else + { + tangent.Set(flipX ? -tangents[t].x : tangents[t].x, + flipY ? -tangents[t].y : tangents[t].y, + flipZ ? -tangents[t].z : tangents[t].z); + tangent = tform.TransformDirection(tangent); + tbuff[t] = new Vector4(tangent.x, tangent.y, tangent.z, tangents[t].w); + } + } + } + } + } + + static void CalculateUVPadding(ExportSceneData data, AdjustUVPaddingData adata) + { + var meshToPaddingMap = adata.meshToPaddingMap; + var meshToObjIDs = adata.meshToObjIDs; + + float smallestMapScale = 1; + float colorScale = 1.0f / (1 << (int)((1.0f - ftBuildGraphics.mainLightmapScale) * 6)); + float maskScale = 1.0f / (1 << (int)((1.0f - ftBuildGraphics.maskLightmapScale) * 6)); + float dirScale = 1.0f / (1 << (int)((1.0f - ftBuildGraphics.dirLightmapScale) * 6)); + smallestMapScale = Mathf.Min(colorScale, maskScale); + smallestMapScale = Mathf.Min(smallestMapScale, dirScale); + + var objsToWrite = data.objsToWrite; + var objsToWriteGroup = data.objsToWriteGroup; + var objsToWriteVerticesPosW = data.objsToWriteVerticesPosW; + var objsToWriteIndices = data.objsToWriteIndices; + var objsToWriteHolder = data.objsToWriteHolder; + + // Calculate every implicit mesh area and convert to proper padding value + var explicitGroupTotalArea = new Dictionary<int, float>(); + var objsWithExplicitGroupPadding = new List<int>(); + var objsWithExplicitGroupPaddingWidth = new List<float>(); + + for(int i=0; i<objsToWrite.Count; i++) + { + var lmgroup = objsToWriteGroup[i]; + if (lmgroup == null) continue; + if (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) continue; // no need to adjust for vertex-baked meshes + var prefabParent = PrefabUtility.GetPrefabParent(objsToWrite[i]) as GameObject; + if (prefabParent == null) continue; + var sharedMesh = GetSharedMesh(objsToWrite[i]); + var assetPath = AssetDatabase.GetAssetPath(sharedMesh); + var importer = AssetImporter.GetAtPath(assetPath) as ModelImporter; + if (importer == null || !importer.generateSecondaryUV) continue; + // user doesn't care much about UVs - adjust + + var m = sharedMesh; + var vpos = objsToWriteVerticesPosW[i]; + float area = 0; + var inds = objsToWriteIndices[i]; + for(int k=0;k<m.subMeshCount;k++) { + var indices = inds[k];// m.GetTriangles(k); + int indexA, indexB, indexC; + for(int j=0;j<indices.Length;j+=3) + { + indexA = indices[j]; + indexB = indices[j + 1]; + indexC = indices[j + 2]; + + var v1 = vpos[indexA]; + var v2 = vpos[indexB]; + var v3 = vpos[indexC]; + area += Vector3.Cross(v2 - v1, v3 - v1).magnitude; + } + } + //var so = new SerializedObject(objsToWrite[i].GetComponent<Renderer>()); + //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<BakeryLightmapGroupSelector>(); + 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<int> arr; + if (!meshToObjIDs.TryGetValue(m, out arr)) + { + meshToObjIDs[m] = arr = new List<int>(); + } + if (!arr.Contains(i)) arr.Add(i); + } + + for(int j=0; j<objsWithExplicitGroupPadding.Count; j++) + { + int i = objsWithExplicitGroupPadding[j]; + float width = objsWithExplicitGroupPaddingWidth[j]; + var lmgroup = objsToWriteGroup[i]; + float totalArea = explicitGroupTotalArea[lmgroup.id]; + float twidth = (width / Mathf.Sqrt(totalArea)) * lmgroup.resolution; + var m = GetSharedMesh(objsToWrite[i]); + + // Following is copy-pasted from the loop above + 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<int> arr; + if (!meshToObjIDs.TryGetValue(m, out arr)) + { + meshToObjIDs[m] = arr = new List<int>(); + } + 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<storages.Length; s++) + { + var str = storages[s]; + if (str == null) continue; + str.modifiedAssetPathList = new List<string>(); + str.modifiedAssets = new List<ftGlobalStorage.AdjustedMesh>(); + } + } + + 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<sceneCount; s++) + { + var objStorage = gstorage;// == null ? storages[0] : gstorage;// storages[s]; + int mstoreIndex = objStorage.modifiedAssetPathList.IndexOf(assetPath); + int ind = -1; + var mname = m.name; + if (mstoreIndex >= 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<string>(); + newStruct.padding = new List<int>(); + 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<int>(); + 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<ids.Count; xx++) dirtyObjList.Add(ids[xx]); +#if UNITY_2017_1_OR_NEWER + objStorage.SyncModifiedAsset(mstoreIndex); +#endif + } + else + { + 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<int>(); + 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<ids.Count; xx++) dirtyObjList.Add(ids[xx]); + paddingList[ind] = requiredPaddingClamped; + unwrapperList[ind] = (int)ftRenderLightmap.unwrapper; +#if UNITY_2017_1_OR_NEWER + objStorage.SyncModifiedAsset(mstoreIndex); +#endif + } + } + + // Backup padding storage to scene + for(int s=0; s<storages.Length; s++) + { + var str = storages[s]; + if (str == null) continue; + var localIndex = str.modifiedAssetPathList.IndexOf(assetPath); + if (localIndex < 0) + { + str.modifiedAssetPathList.Add(assetPath); + str.modifiedAssets.Add(objStorage.modifiedAssets[mstoreIndex]); + } + else + { + str.modifiedAssets[localIndex] = objStorage.modifiedAssets[mstoreIndex]; + } + } + } + } + + EditorUtility.SetDirty(gstorage); + } + + static bool ValidatePaddingImmutability(AdjustUVPaddingData adata) + { + if (validateLightmapStorageImmutability) + { + if (adata.dirtyAssetList.Count > 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<objsToWrite.Count; i++) + { + var obj = objsToWrite[i]; + var lmgroup = objsToWriteGroup[i]; + var holderObj = objsToWriteHolder[i]; + if (holderObj != null) + { + if (!holderRect.TryGetValue(holderObj, out rc)) + { + holderObj = null; + } + } + var scaleOffset = holderObj == null ? emptyVec4 : new Vector4(rc.width, rc.height, rc.x, rc.y); + + var sceneID = sceneToID[obj.scene]; + var st = storages[sceneID]; + if (st == null) + { + Debug.LogError("ValidateScaleOffsetImmutability: no storage"); + return false; + } + + var storedScaleOffset = Vector4.zero; + if (obj.name == "__ExportTerrain") + { + var tindex = terrainObjectList.IndexOf(obj.transform.parent.gameObject); + var terrain = terrainObjectToActual[tindex]; + int index = st.bakedRenderersTerrain.IndexOf(terrain); + /*if (st.bakedIDsTerrain[index] != lmgroup.id) + { + Debug.LogError("ValidateScaleOffsetImmutability: terrain LMID does not match"); + return false; + }*/ + if (index < 0 || st.bakedScaleOffsetTerrain.Count <= index) continue; + storedScaleOffset = st.bakedScaleOffsetTerrain[index]; + } + else + { + int index = st.bakedRenderers.IndexOf(GetValidRenderer(obj)); + /*if (st.bakedIDs[index] != lmgroup.id) + { + Debug.LogError("ValidateScaleOffsetImmutability: LMID does not match"); + Debug.LogError(st.bakedIDs[index]+" "+lmgroup.id+" "+lmgroup.name); + return false; + }*/ + if (index < 0 || st.bakedScaleOffset.Count <= index) continue; + storedScaleOffset = st.bakedScaleOffset[index]; + } + // approx equality + if (!(scaleOffset == storedScaleOffset)) + { + Debug.LogError("ValidateScaleOffsetImmutability: scale/offset does not match on " + obj.name); + Debug.Log("(" + scaleOffset.x + ", " + scaleOffset.y + "," + scaleOffset.z + ", " + scaleOffset.w + ") vs (" + + storedScaleOffset.x + ", " + storedScaleOffset.y + "," + storedScaleOffset.z + ", " + storedScaleOffset.w + ")" + ); + return false; + } + } + } + return true; + } + + static bool ClearUVPadding(ExportSceneData data, AdjustUVPaddingData adata) + { + var objsToWrite = data.objsToWrite; + var dirtyAssetList = adata.dirtyAssetList; + var dirtyObjList = adata.dirtyObjList; + + for(int i=0; i<objsToWrite.Count; i++) + { + var sharedMesh = GetSharedMesh(objsToWrite[i]); + var assetPath = AssetDatabase.GetAssetPath(sharedMesh); + + int mstoreIndex = gstorage.modifiedAssetPathList.IndexOf(assetPath); + if (mstoreIndex < 0) continue; + + dirtyObjList.Add(i); + if (!dirtyAssetList.Contains(assetPath)) + { + dirtyAssetList.Add(assetPath); + } + } + + if (!ValidatePaddingImmutability(adata)) return false; + + for(int i=0; i<dirtyAssetList.Count; i++) + { + var assetPath = dirtyAssetList[i]; + int mstoreIndex = gstorage.modifiedAssetPathList.IndexOf(assetPath); + DebugLogInfo("Reimport " + assetPath); + ProgressBarShow("Exporting scene - clearing UV adjustment for " + assetPath + "...", 0, false); + + gstorage.ClearAssetModifications(mstoreIndex); + } + + EditorUtility.SetDirty(gstorage); + + return true; + } + + static bool ReimportModifiedAssets(AdjustUVPaddingData adata) + { + var dirtyAssetList = adata.dirtyAssetList; + + for(int i=0; i<dirtyAssetList.Count; i++) + { + var assetPath = dirtyAssetList[i]; + DebugLogInfo("Reimport " + assetPath); + ProgressBarShow("Exporting scene - adjusting UV padding for " + assetPath + "...", 0, false); + //AssetDatabase.ImportAsset(assetPath); + (AssetImporter.GetAtPath(assetPath) as ModelImporter).SaveAndReimport(); + + if (CheckUnwrapError()) + { + return false; + } + } + return true; + } + + static void TransformModifiedAssets(ExportSceneData data, AdjustUVPaddingData adata, bool tangentSHLights) + { + // Transform modified vertices to world space again + for(int d=0; d<adata.dirtyObjList.Count; d++) + { + int i = adata.dirtyObjList[d]; + + // Refresh attributes and indices after reimport + bool isSkin; + var m = GetSharedMeshSkinned(data.objsToWrite[i], out isSkin); + data.objsToWriteVerticesUV[i] = m.uv; + data.objsToWriteVerticesUV2[i] = m.uv2; + var inds = new int[m.subMeshCount][]; + for(int n=0; n<inds.Length; n++) inds[n] = m.GetTriangles(n); + data.objsToWriteIndices[i] = inds; + + TransformVertices(data, tangentSHLights, i); // because vertex count/order could be modified + } + } + + static void CalculateHolderUVBounds(ExportSceneData data) + { + var objsToWrite = data.objsToWrite; + var objsToWriteGroup = data.objsToWriteGroup; + var objsToWriteHolder = data.objsToWriteHolder; + var objsToWriteVerticesPosW = data.objsToWriteVerticesPosW; + var objsToWriteVerticesUV = data.objsToWriteVerticesUV; + var objsToWriteVerticesUV2 = data.objsToWriteVerticesUV2; + var objsToWriteIndices = data.objsToWriteIndices; + var objToLodLevel = data.objToLodLevel; + var holderObjUVBounds = data.holderObjUVBounds; + var holderObjArea = data.holderObjArea; + var groupToHolderObjects = data.groupToHolderObjects; + + // Calculate implicit group / atlas packing data + // UV bounds and worldspace area + for(int i=0; i<objsToWrite.Count; i++) + { + var obj = objsToWrite[i]; + var lmgroup = objsToWriteGroup[i]; + var calculateArea = lmgroup == null ? false : (lmgroup.isImplicit || lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas); + if (!calculateArea) continue; + + var holderObj = objsToWriteHolder[i]; + var m = GetSharedMesh(obj); + //var mr = obj.GetComponent<Renderer>(); + + var vpos = objsToWriteVerticesPosW[i]; + var vuv = objsToWriteVerticesUV2[i];//m.uv2; + var inds = objsToWriteIndices[i]; + //if (vuv.Length == 0 || obj.GetComponent<BakeryLightMesh>()!=null) vuv = objsToWriteVerticesUV[i];//m.uv; // area lights or objects without UV2 export UV1 instead + if (vuv.Length == 0 || obj.GetComponent<BakeryLightMesh>()!=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<m.subMeshCount;k++) { + + var indices = inds[k];//m.GetTriangles(k); + int indexA, indexB, indexC; + float area = 0; + //float areaUV = 0; + Vector4 uvBounds = new Vector4(1,1,0,0); // minx, miny, maxx, maxy + + for(int j=0;j<indices.Length;j+=3) + { + indexA = indices[j]; + indexB = indices[j + 1]; + indexC = indices[j + 2]; + + var v1 = vpos[indexA]; + var v2 = vpos[indexB]; + var v3 = vpos[indexC]; + area += Vector3.Cross(v2 - v1, v3 - v1).magnitude; + + if (vuv.Length > 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<GameObject> holderList; + if (!groupToHolderObjects.TryGetValue(lmgroup, out holderList)) + { + groupToHolderObjects[lmgroup] = holderList = new List<GameObject>(); + } + 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<groupList.Count; i++) + { + var lmgroup = groupList[i]; + if (lmgroup.isImplicit) + { + lmgroup.resolution = ResolutionFromArea(lmgroup.area); + } + } + } + + static void NormalizeHolderArea(BakeryLightmapGroup lmgroup, List<GameObject> 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.Count; i++) + { + // space outside of UV bounds shouldn't affect area + var uvbounds = holderObjUVBounds[holderObjs[i]]; + var width = uvbounds.z - uvbounds.x; + var height = uvbounds.w - uvbounds.y; + float uvboundsArea = width * height; + + lmgroupArea += holderObjArea[holderObjs[i]] * uvboundsArea; + } + areaMult = 1.0f / lmgroupArea; + } + + // Perform the division and sum up total UV area + for(int i=0; i<holderObjs.Count; i++) + { + holderObjArea[holderObjs[i]] *= areaMult; + } + } + + static void SumHolderAreaPerLODLevel(List<GameObject> holderObjs, ExportSceneData data, PackData pdata) + { + var objToLodLevel = data.objToLodLevel; + var holderObjArea = data.holderObjArea; + var remainingAreaPerLodLevel = pdata.remainingAreaPerLodLevel; + + for(int i=0; i<holderObjs.Count; i++) + { + int lodLevel = -1; + if (!objToLodLevel.TryGetValue(holderObjs[i], out lodLevel)) lodLevel = -1; + + float lodArea = 0; + if (!remainingAreaPerLodLevel.TryGetValue(lodLevel, out lodArea)) lodArea = 0; + + remainingAreaPerLodLevel[lodLevel] = lodArea + holderObjArea[holderObjs[i]]; + } + } + + static int CompareGameObjectsForPacking(GameObject a, GameObject b) + { + if (splitByScene) + { + if (a.scene.name != b.scene.name) return a.scene.name.CompareTo(b.scene.name); + } + + if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap) + { + bool ba = a.name != "__ExportTerrainParent"; + bool bb = b.name != "__ExportTerrainParent"; + if (ba != bb) return ba.CompareTo(bb); + } + + int lodLevelA = -1; + int lodLevelB = -1; + if (!cmp_objToLodLevel.TryGetValue(a, out lodLevelA)) lodLevelA = -1; + if (!cmp_objToLodLevel.TryGetValue(b, out lodLevelB)) lodLevelB = -1; + + if (lodLevelA != lodLevelB) return lodLevelA.CompareTo(lodLevelB); + + float areaA = cmp_holderObjArea[a]; + float areaB = cmp_holderObjArea[b]; + + // Workaround for "override resolution" + // Always pack such rectangles first + var comp = a.GetComponent<BakeryLightmapGroupSelector>(); + if (comp != null && comp.instanceResolutionOverride) areaA = comp.instanceResolution * 10000; + + comp = b.GetComponent<BakeryLightmapGroupSelector>(); + 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<GameObject> 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<holderObjs.Count; i++) + { + var area = holderObjArea[holderObjs[i]]; + var uvbounds = holderObjUVBounds[holderObjs[i]]; + + // Calculate width and height of each holder in atlas UV space + float width, height; + var comp = holderObjs[i].GetComponent<BakeryLightmapGroupSelector>(); + 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<autoAtlasGroups.Count; g++) + { + if (splitByScene) + { + if (autoAtlasGroups[g].sceneName != holderObjs[i].scene.name) continue; + } + if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap) + { + bool ba = holderObjs[i].name != "__ExportTerrainParent"; + bool bb = !autoAtlasGroups[g].containsTerrains; + if (ba != bb) continue; + } + if (autoAtlasGroups[g].sceneLodLevel != lodLevel) continue; + twidth = (width * texelsPerUnit) / autoAtlasGroups[g].resolution; + theight = (height * texelsPerUnit) / autoAtlasGroups[g].resolution; + //unclampedTwidth = twidth; + //unclampedTheight = twidth; + twidth = twidth > 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<BakeryLightmapGroup>(); + 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<storages[sceneToID[holder.scene]].implicitGroupedObjects.Count; k++) + { + if (storages[sceneToID[holder.scene]].implicitGroupedObjects[k] == holder) + { + storages[sceneToID[holder.scene]].implicitGroups[k] = newGroup; + //Debug.LogError("Implicit set: " + k+" "+newGroup.name+" "+holder.name); + } + } + */ + //lmgroup = newGroup; + } + else + { + if (!pdata.repackStage2) + { + // explicit packed atlas - can't fit - try shrinking the whole atlas + pdata.repackTries++; + if (pdata.repackTries < atlasMaxTries) + { + pdata.repack = true; + pdata.repackScale *= 0.75f; + //Debug.LogError("Can't fit, set " +repackScale); + break; + } + } + else + { + // explicit packed atlas - did fit, now trying to scale up, doesn't work - found optimal fit + pdata.repack = true; + pdata.repackScale /= atlasScaleUpValue;//*= 0.75f; + //Debug.LogError("Final, set " +repackScale); + pdata.finalRepack = true; + pdata.repackTries = 0; + break; + } + } + } + + if (node == null) + { + // No way to fit + ExportSceneError("Can't fit " + holderObjs[i].name+" "+rect.width+" "+rect.height); + return false; + } + else + { + // Generate final rectangle to transform local UV -> 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<storages[sceneToID[holder.scene]].implicitGroupedObjects.Count; k++) + { + if (storages[sceneToID[holder.scene]].implicitGroupedObjects[k] == holder) + { + storages[sceneToID[holder.scene]].implicitGroups[k] = newGroup; + //Debug.LogError("Implicit set: " + k+" "+newGroup.name+" "+holder.name); + } + } + } + + static List<int> GetAtlasBucketRanges(List<GameObject> holderObjs, ExportSceneData data, bool onlyUserSplits) + { + var objToLodLevel = data.objToLodLevel; + + var ranges = new List<int>(); + 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.Count; i++) + { + bool splitAtlas = false; + + // Split by scene + if (splitByScene) + { + var objSceneName = holderObjs[i].scene.name; + if (objSceneName != sceneName) + { + splitAtlas = true; + sceneName = objSceneName; + } + } + + if (!onlyUserSplits) + { + // Split by LOD + int objLodLevel; + if (!objToLodLevel.TryGetValue(holderObjs[i], out objLodLevel)) objLodLevel = -1; + if (objLodLevel != lodLevel) + { + lodLevel = objLodLevel; + splitAtlas = true; + } + + // Split by terrain + if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap) + { + bool ba = holderObjs[i].name == "__ExportTerrainParent"; + if (ba != isTerrain) + { + isTerrain = ba; + splitAtlas = true; + } + } + } + + if (splitAtlas) + { + end = i; + ranges.Add(start); + ranges.Add(end-1); + start = end; + } + } + } + end = holderObjs.Count-1; + ranges.Add(start); + ranges.Add(end); + return ranges; + } + + static float SumObjectsArea(List<GameObject> 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<count; i++) + { + // Create additional lightmaps + newGroup = ScriptableObject.CreateInstance<BakeryLightmapGroup>(); + 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<GameObject> 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<int> buckets = null; + if (lmgroup.isImplicit) + { + buckets = GetAtlasBucketRanges(holderObjs, data, postPacking); + bucketCount = buckets.Count; + } + + var holderAutoIndex = new int[holderObjs.Count]; + + for(int bucket=0; bucket<bucketCount; bucket+=2) + { + if (lmgroup.isImplicit) + { + bStart = buckets[bucket]; + bEnd = buckets[bucket+1]; + } + int bSize = bEnd - bStart; + if (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<BakeryLightmapGroupSelector>(); + 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<int> indexList = null; + List<Vector2> uvList = null; + vertCount = indexCount = 0; + int numMeshes = 0; + var ubounds = holderObjUVBounds[holderObjs[i]]; + var holder = holderObjs[i]; + for(int o=0; o<objsToWriteHolder.Count; o++) + { + if (objsToWriteHolder[o] != holder) continue; + + if (numMeshes == 1) + { + indexList = new List<int>(); + uvList = new List<Vector2>(); + for(int j=0; j<indices.Length; j++) + { + indexList.Add(indices[j]); + } + for(int j=0; j<uv.Length; j++) + { + uvList.Add(uv[j]); + } + } + + bool isSkin; + var mesh = GetSharedMeshSkinned(objsToWrite[o], out isSkin); + indices = mesh.triangles; + var uv1 = mesh.uv; + var uv2 = mesh.uv2; + if (uv2 == null || uv2.Length == 0) + { + uv = uv1; + } + else + { + uv = uv2; + } + for(int t=0; t<uv.Length; t++) + { + float u = (uv[t].x - ubounds.x) / (ubounds.z - ubounds.x); + float v = (uv[t].y - ubounds.y) / (ubounds.w - ubounds.y); + u *= twidth; + v *= theight; + uv[t] = new Vector2(u, v); + } + + if (numMeshes > 0) + { + for(int j=0; j<indices.Length; j++) + { + indexList.Add(indices[j] + vertCount); + } + for(int j=0; j<uv.Length; j++) + { + uvList.Add(uv[j]); + } + } + + vertCount += uv.Length; + indexCount += indices.Length; + numMeshes++; + } + if (numMeshes > 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<newVertCount; j++) + { + if (uvBuffer[j].x < minU) minU = uvBuffer[j].x; + if (uvBuffer[j].y < minV) minV = uvBuffer[j].y; + if (uvBuffer[j].x > 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<int>[autoAtlasGroups.Count]; + for(int bucket=0; bucket<bucketCount; bucket+=2) + { + bStart = buckets[bucket]; + bEnd = buckets[bucket+1]; + for(int i=bStart; i<=bEnd; i++) + { + int autoLM = holderAutoIndex[i]; + if (autoLMBuckets[autoLM] == null) autoLMBuckets[autoLM] = new List<int>(); + if (!autoLMBuckets[autoLM].Contains(bucket)) autoLMBuckets[autoLM].Add(bucket); + } + } + int origGroupCount = autoAtlasGroups.Count; + for(int i=0; i<origGroupCount; i++) + { + if (autoLMBuckets[i] != null && autoLMBuckets[i].Count > 1) + { + // Split + for(int j=1; j<autoLMBuckets[i].Count; j++) + { + autoAtlasGroups[i].sceneName = holderObjs[bStart].scene.name; + + var newGroup = AllocateAutoAtlas(1, autoAtlasGroups[i], data); + int bucket = autoLMBuckets[i][j]; + bStart = buckets[bucket]; + bEnd = buckets[bucket+1]; + + // Update LMGroup data + //newGroup.sceneName = holderObjs[bStart].scene.name; + int lodLevel; + if (!objToLodLevel.TryGetValue(holderObjs[bStart], out lodLevel)) lodLevel = -1; + newGroup.sceneLodLevel = lodLevel; + if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap) + { + newGroup.containsTerrains = holderObjs[bStart].name == "__ExportTerrainParent"; + } + newGroup.parentName = autoAtlasGroups[i].name; + autoAtlasGroups[i].parentName = "|"; + //Debug.LogError(autoAtlasGroups[i].name+" (" +autoAtlasGroups[i].id+") -> "+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<origGroupCount; i++) + { + if (autoLMBuckets[i] != null) + { + for(int j=0; j<holderObjs.Count; j++) + { + if (holderAutoIndex[j] != i) continue; + + // Update LMGroup data + var newGroup = autoAtlasGroups[i]; + newGroup.sceneName = holderObjs[j].scene.name; + int lodLevel; + if (!objToLodLevel.TryGetValue(holderObjs[j], out lodLevel)) lodLevel = -1; + newGroup.sceneLodLevel = lodLevel; + if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap) + { + newGroup.containsTerrains = holderObjs[j].name == "__ExportTerrainParent"; + } + + break; + } + } + } + } + else if (bucketCount > 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<bucketCount; bucket+=2) + { + bStart = buckets[bucket]; + bEnd = buckets[bucket+1]; + + var newGroup = AllocateAutoAtlas(1, lmgroup, data); + + if (!objToLodLevel.TryGetValue(holderObjs[bStart], out lodLevel)) lodLevel = -1; + newGroup.sceneLodLevel = lodLevel; + if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap) + { + newGroup.containsTerrains = holderObjs[bStart].name == "__ExportTerrainParent"; + } + newGroup.mode = lmgroup.mode; + newGroup.parentName = lmgroup.name; + lmgroup.parentName = "|"; + + //Debug.LogError(newGroup.name+": "+ newGroup.sceneLodLevel+" because of " + holderObjs[bStart].name); + + for(int k=bStart; k<=bEnd; k++) + { + //MoveObjectToImplicitGroup(holderObjs[k], newGroup, data); + data.storages[data.sceneToID[holderObjs[k].scene]].implicitGroupedObjects.Add(holderObjs[k]); + data.storages[data.sceneToID[holderObjs[k].scene]].implicitGroups.Add(newGroup); + tempStorage.implicitGroupMap[holderObjs[k]] = newGroup; + } + } + } + } + + return true; + } + + static void NormalizeAtlas(BakeryLightmapGroup lmgroup, List<GameObject> 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<holderObjs.Count; i++) + { + var rect = holderRect[holderObjs[i]]; + if ((rect.x + rect.width) > 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<holderObjs.Count; i++) + { + var rect = holderRect[holderObjs[i]]; + holderRect[holderObjs[i]] = new Rect(rect.x * normalizeScale, rect.y * normalizeScale, rect.width * normalizeScale, rect.height * normalizeScale); + } + } + } + + static bool PackAtlases(ExportSceneData data) + { + // IN, OUT lmgroup.containsTerrains, OUT holderObjs (sort) + var groupToHolderObjects = data.groupToHolderObjects; + + // IN + var objToLodLevel = data.objToLodLevel; // LODs packed to separate atlases + cmp_objToLodLevel = objToLodLevel; + + // IN/OUT + var holderObjArea = data.holderObjArea; // performs normalization + cmp_holderObjArea = holderObjArea; + + // Pack atlases + // Try to scale all objects to occupy all atlas space + foreach(var pair in groupToHolderObjects) + { + // For every LMGroup with PackAtlas mode + var lmgroup = pair.Key; + var holderObjs = pair.Value; // get all objects + + var pdata = new PackData(); + + // Normalize by worldspace area and uv area + // Read/write holderObjArea + NormalizeHolderArea(lmgroup, holderObjs, data); + + // Sort objects by area and scene LOD level + // + optionally by scene + // + split by terrain + holderObjs.Sort(CompareGameObjectsForPacking); + + var packer = lmgroup.atlasPacker == BakeryLightmapGroup.AtlasPacker.Auto ? atlasPacker : (ftGlobalStorage.AtlasPacker)lmgroup.atlasPacker; + if (packer == ftGlobalStorage.AtlasPacker.xatlas) + { + if (!PackWithXatlas(lmgroup, holderObjs, data, pdata)) + { + ExportSceneError("Failed packing atlas"); + return false; + } + } + else + { + // Calculate area sum for every scene LOD level in LMGroup + // Write remainingAreaPerLodLevel + SumHolderAreaPerLODLevel(holderObjs, data, pdata); + + // Perform recursive packing + while(pdata.repack) + { + pdata.continueRepack = true; + if (!Pack(lmgroup, holderObjs, data, pdata)) return false; + if (!pdata.continueRepack) break; + } + // Normalize atlas by largest axis + NormalizeAtlas(lmgroup, holderObjs, data, pdata); + } + } + cmp_objToLodLevel = null; + cmp_holderObjArea = null; + + return true; + } + + static void NormalizeAutoAtlases(ExportSceneData data) + { + var autoAtlasGroups = data.autoAtlasGroups; + var autoAtlasGroupRootNodes = data.autoAtlasGroupRootNodes; + var holderRect = data.holderRect; + + // Normalize autoatlases + var stack = new Stack<AtlasNode>(); + for(int g=0; g<autoAtlasGroups.Count; g++) + { + var lmgroup = autoAtlasGroups[g]; + if (lmgroup.parentName != null && lmgroup.parentName.Length > 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<AtlasNode>(); + + // Join autoatlases + var autoAtlasCategories = new List<string>(); + bool joined = false; + for(int g=0; g<autoAtlasGroups.Count; g++) + { + if (autoAtlasGroups[g].parentName != null && autoAtlasGroups[g].parentName.Length > 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<autoAtlasCategories.Count; alod++) + { + var cat = autoAtlasCategories[alod]; + var autoAtlasSizes = new List<int>(); + var atlasStack = new Stack<BakeryLightmapGroup>(); + for(int g=0; g<autoAtlasGroups.Count; g++) + { + if (autoAtlasGroups[g].parentName != null && autoAtlasGroups[g].parentName.Length > 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<autoAtlasSizes.Count; s++) + { + int asize = autoAtlasSizes[s]; + atlasStack.Clear(); + for(int g=0; g<autoAtlasGroups.Count; g++) + { + if (autoAtlasGroups[g].parentName != null && autoAtlasGroups[g].parentName.Length > 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<BakeryLightmapGroup>(); + 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<groupList.Count; x++) + { + groupList[x].id--; + data.lmid--; + } + + // Modify implicit group storage + joined = true; + stack.Clear(); + stack.Push(subgroupRootNode); + while(stack.Count > 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<storages[sceneToID[node.obj.scene]].implicitGroupedObjects.Count; k++) + { + if (storages[sceneToID[node.obj.scene]].implicitGroupedObjects[k] == node.obj) + { + storages[sceneToID[node.obj.scene]].implicitGroups[k] = newGroup; + //Debug.LogError("Implicit set (join): " + k+" "+newGroup.name); + } + } + */ + } + if (node.child0 != null) stack.Push(node.child0); + if (node.child1 != null) stack.Push(node.child1); + } + } + } + } + } + } + if (joined) + { + for(int i=0; i<objsToWrite.Count; i++) + { + objsToWriteGroup[i] = GetLMGroupFromObject(objsToWrite[i], data); + } + } + } + + static bool ExportSceneValidationMessage(string msg) + { + ProgressBarEnd(false); + if (ftRenderLightmap.verbose) + { + if (!EditorUtility.DisplayDialog("Bakery", msg, "Continue anyway", "Cancel")) + { + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + return false; + } + } + else + { + Debug.LogError(msg); + } + ProgressBarInit("Exporting scene - preparing..."); + return true; + } + + static void ExportSceneError(string phase) + { + DebugLogError("Error exporting scene (" + phase + ") - see console for details"); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + } + + class ExportSceneData + { + // Per-scene data + public ftLightmapsStorage[] storages; + public int firstNonNullStorage; + //public ftLightmapsStorage settingsStorage; + public Dictionary<Scene, int> sceneToID = new Dictionary<Scene, int>(); + public Dictionary<Scene,bool> sceneHasStorage = new Dictionary<Scene,bool>(); + + // Object properties + public Dictionary<GameObject,int> objToLodLevel = new Dictionary<GameObject,int>(); // defines atlas LOD level + public Dictionary<GameObject,List<int>> objToLodLevelVisible = new Dictionary<GameObject,List<int>>(); // defines LOD levels where this object is visible + public Dictionary<GameObject, float> objToScaleInLm = new Dictionary<GameObject, float>(); + + public List<GameObject> objsToWrite = new List<GameObject>(); + public List<bool> objsToWriteLightmapped = new List<bool>(); + public List<BakeryLightmapGroup> objsToWriteGroup = new List<BakeryLightmapGroup>(); + public List<GameObject> objsToWriteHolder = new List<GameObject>(); + public List<Vector4> objsToWriteScaleOffset = new List<Vector4>(); + public List<Vector2[]> objsToWriteUVOverride = new List<Vector2[]>(); + public List<string> objsToWriteNames = new List<string>(); + public List<Vector3[]> objsToWriteVerticesPosW = new List<Vector3[]>(); + public List<Vector3[]> objsToWriteVerticesNormalW = new List<Vector3[]>(); + public List<Vector4[]> objsToWriteVerticesTangentW = new List<Vector4[]>(); + public List<Vector2[]> objsToWriteVerticesUV = new List<Vector2[]>(); + public List<Vector2[]> objsToWriteVerticesUV2 = new List<Vector2[]>(); + public List<int[][]> objsToWriteIndices = new List<int[][]>(); + public List<bool> objsToWriteHasMetaAlpha = new List<bool>(); + + public List<Renderer> outsideRenderers = new List<Renderer>(); // for sector+SRP only + + // Auto-atlasing + public List<BakeryLightmapGroup> autoAtlasGroups = new List<BakeryLightmapGroup>(); + public List<AtlasNode> autoAtlasGroupRootNodes = new List<AtlasNode>(); + public BakeryLightmapGroup autoVertexGroup; + + // Data to collect for atlas packing + public Dictionary<GameObject, float> holderObjArea = new Dictionary<GameObject, float>(); // LMGroup holder area, accumulated from all children + public Dictionary<GameObject, Vector4> holderObjUVBounds = new Dictionary<GameObject, Vector4>(); // LMGroup holder 2D UV AABB + public Dictionary<BakeryLightmapGroup, List<GameObject>> groupToHolderObjects = new Dictionary<BakeryLightmapGroup, List<GameObject>>(); // LMGroup -> holders map + public Dictionary<GameObject, Rect> holderRect = new Dictionary<GameObject, Rect>(); + + // Per-LMGroup data + public List<BakeryLightmapGroup> groupList = new List<BakeryLightmapGroup>(); + public List<Bounds> lmBounds = new List<Bounds>(); // list of bounding boxes around LMGroups for testing lights + + // Geometry data + public List<int>[] indicesOpaqueLOD = null; + public List<int>[] indicesTransparentLOD = null; + + public int lmid = 0; // LMID counter + + public ExportSceneData(int sceneCount) + { + storages = new ftLightmapsStorage[sceneCount]; + } + } + + class AdjustUVPaddingData + { + public List<int> dirtyObjList = new List<int>(); + public List<string> dirtyAssetList = new List<string>(); + public Dictionary<Mesh, List<int>> meshToObjIDs = new Dictionary<Mesh, List<int>>(); + public Dictionary<Mesh, int> meshToPaddingMap = new Dictionary<Mesh, int>(); + } + + class PackData + { + public Dictionary<int,float> remainingAreaPerLodLevel = new Dictionary<int,float>(); + 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<int>(); + var indicesTransparent = new List<int>(); + + var data = new ExportSceneData(sceneCount); + + bool tangentSHLights = CheckForTangentSHLights(); + + // Per-LMGroup data + var lmAlbedoList = new List<IntPtr>(); // list of albedo texture for UV GBuffer rendering + var lmAlbedoListTex = new List<Texture>(); + var lmAlphaList = new List<IntPtr>(); // list of alpha textures for alpha buffer generation + var lmAlphaListRAM = new List<TexInput>(); // non-DX11 array + var lmAlphaListTex = new List<Texture>(); + var lmAlphaRefList = new List<float>(); // list of alpha texture refs + var lmAlphaChannelList = new List<int>(); // list of alpha channels + + // lod-related + var lmVOffset = new List<int>(); + var lmUVArrays = new List<List<float>>(); + var lmUVArrays2 = new List<float[]>(); + var lmUVArrays3 = new List<float[]>(); + var lmIndexArrays = new List<List<int>>(); + var lmIndexArrays2 = new List<int[]>(); + var lmLocalToGlobalIndices = new List<List<int>>(); + + vbtraceTexPosNormalArray = new List<float>(); + vbtraceTexUVArray = new List<float>(); + + 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<allSectors.Length; i++) + { + if (allSectors[i].previewEnabled) BakerySectorInspector.DisablePreview(allSectors[i]); + } + } + + // Init storages + try + { + InitSceneStorage(data); + } + catch(Exception e) + { + ExportSceneError("Global storage init"); + Debug.LogError("Exception caught: " + e.ToString()); + throw; + } + + // Create LMGroup for light probes + if (ftRenderLightmap.lightProbeMode == ftRenderLightmap.LightProbeMode.L1 && renderTextures && !atlasOnly && ftRenderLightmap.hasAnyProbes && !ftRenderLightmap.fullSectorRender) + { + var c = CreateLightProbeLMGroup(data); + while(c.MoveNext()) yield return null; + } + + if (ftRenderLightmap.hasAnyVolumes) + { + var c2 = CreateVolumeLMGroup(data); + while(c2.MoveNext()) yield return null; + } + + // wip + var lmBounds = data.lmBounds; + var storages = data.storages; + var sceneToID = data.sceneToID; + var groupList = data.groupList; + var objToLodLevel = data.objToLodLevel; + var objToLodLevelVisible = data.objToLodLevelVisible; + var objsToWrite = data.objsToWrite; + var objsToWriteGroup = data.objsToWriteGroup; + var objsToWriteHolder = data.objsToWriteHolder; + var objsToWriteIndices = data.objsToWriteIndices; + var objsToWriteNames = data.objsToWriteNames; + var objsToWriteUVOverride = data.objsToWriteUVOverride; + var objsToWriteScaleOffset = data.objsToWriteScaleOffset; + var objsToWriteVerticesUV = data.objsToWriteVerticesUV; + var objsToWriteVerticesUV2 = data.objsToWriteVerticesUV2; + var objsToWriteVerticesPosW = data.objsToWriteVerticesPosW; + var objsToWriteVerticesNormalW = data.objsToWriteVerticesNormalW; + var objsToWriteVerticesTangentW = data.objsToWriteVerticesTangentW; + var objsToWriteHasMetaAlpha = data.objsToWriteHasMetaAlpha; + var holderRect = data.holderRect; + + terrainObjectList = new List<GameObject>(); + terrainObjectToActual = new List<Terrain>(); + terrainObjectToHeightMap = new List<Texture>(); + terrainObjectToHeightMapRAM = new List<TexInput>(); + terrainObjectToBounds = new List<float>(); + terrainObjectToBoundsUV = new List<float>(); + terrainObjectToFlags = new List<int>(); + terrainObjectToLMID = new List<int>(); + terrainObjectToHeightMips = new List<List<float[]>>(); + temporaryGameObjects = new List<GameObject>(); + temporaryAreaLightMeshList = new List<GameObject>(); + temporaryAreaLightMeshList2 = new List<BakeryLightMesh>(); + + 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<GameObject, UnityEngine.Object>(); // 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<cpoints.Count; i++) + { + //if (!GenerateFarSphere(data, sector.cpoints[i].position, objCount)) yield break; + fdata[i] = GenerateFarSphereData(data, cpoints[i].position, objCount, i == 0); + if (fdata[i] == null) + { + DebugLogError("GenerateFarSphereData failed"); + userCanceled = true; + yield break; + } + } + + // Cull each other (closest texels win) + for(int i=0; i<cpoints.Count; i++) + { + for(int j=0; j<cpoints.Count; j++) + { + if (i == j) continue; + if (!ClipFarSphere(fdata[i], fdata[j])) + { + DebugLogError("ClipFarSphere failed"); + userCanceled = true; + yield break; + } + } + } + + // Generate meshes + if (!GenerateFarSpheres(data, fdata, cpoints)) + { + DebugLogError("GenerateFarSpheres failed"); + userCanceled = true; + yield break; + } + + if (sectorCaptureAsset != null) + { + sectorCaptureAsset.meshes = new List<Mesh>(); + sectorCaptureAsset.positions = new List<Vector3>(); + sectorCaptureAsset.textures = new List<Texture2D>(); + for(int i=0; i<cpoints.Count; i++) + { + var fd = fdata[i]; + for(int j=0; j<fd.meshes.Length; j++) + { + sectorCaptureAsset.meshes.Add(fd.meshes[j]); + sectorCaptureAsset.positions.Add(fd.pos); + sectorCaptureAsset.textures.Add(fd.textures[j]); + } + } + sectorCaptureAsset.outsideRenderers = data.outsideRenderers; + yield break; + } + } + else if (sector.captureMode == BakerySector.CaptureMode.LoadCaptured) + { + if (sector.captureAsset == null) + { + DebugLogError("No capture asset is specified for sector " + sector.name); + userCanceled = true; + yield break; + } + else + { + if (!LoadSectorCapture(data, sector.captureAsset, sector.transform)) + { + DebugLogError("Can't load sector capture for " + sector.name); + userCanceled = true; + yield break; + } + } + } + } + + CalculateVertexCountForVertexGroups(data); + CreateAutoAtlasLMGroups(data, renderTextures, atlasOnly); + + TransformVertices(data, tangentSHLights); + + if (_unwrapUVs) + { + var adata = new AdjustUVPaddingData(); + + CalculateUVPadding(data, adata); + ResetPaddingStorageData(data); + StoreNewUVPadding(data, adata); + + if (!ValidatePaddingImmutability(adata)) yield break; + + if (CheckUnwrapError()) yield break; + + // Reimport assets with adjusted padding + if (modifyLightmapStorage) + { + if (!ReimportModifiedAssets(adata)) yield break; + + TransformModifiedAssets(data, adata, tangentSHLights); + } + } + else if (_forceDisableUnwrapUVs) + { + var adata = new AdjustUVPaddingData(); + + ResetPaddingStorageData(data); + if (!ClearUVPadding(data, adata)) yield break; + + if (CheckUnwrapError()) yield break; + + TransformModifiedAssets(data, adata, tangentSHLights); + } + + CalculateHolderUVBounds(data); + CalculateAutoAtlasInitResolution(data); + if (!PackAtlases(data)) yield break; + + if (atlasPacker == ftGlobalStorage.AtlasPacker.Default) + { + NormalizeAutoAtlases(data); + JoinAutoAtlases(data); + } + + if (!ValidateScaleOffsetImmutability(data)) + { + sceneNeedsToBeRebuilt = true; + yield break; + } + + InitSceneStorage2(data); + + //TransformVertices(data); // shouldn't be necessary + + // Update objToWriteGroups because of autoAtlas + if (autoAtlas) + { + for(int i=0; i<objsToWrite.Count; i++) + { + objsToWriteGroup[i] = GetLMGroupFromObject(objsToWrite[i], data); + } + } + + // Done collecting groups + + if (groupList.Count == 0 && modifyLightmapStorage) + { + DebugLogError("You need to mark some objects static or add Bakery Lightmap Group Selector components on them."); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + + if (objsToWrite.Count == 0) + { + DebugLogError("You need to mark some objects static or add Bakery Lightmap Group Selector components on them."); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + + if (atlasOnly) + { + atlasOnlyObj = new List<Renderer>(); + atlasOnlySize = new List<int>(); + atlasOnlyID = new List<int>(); + atlasOnlyScaleOffset = new List<Vector4>(); + var emptyVec4 = new Vector4(1,1,0,0); + Rect rc = new Rect(); + for(int i=0; i<objsToWrite.Count; i++) + { + var lmgroup = objsToWriteGroup[i]; + var holderObj = objsToWriteHolder[i]; + if (holderObj != null) + { + if (!holderRect.TryGetValue(holderObj, out rc)) + { + holderObj = null; + } + } + var scaleOffset = holderObj == null ? emptyVec4 : new Vector4(rc.width, rc.height, rc.x, rc.y); + atlasOnlyObj.Add(GetValidRenderer(objsToWrite[i])); + atlasOnlyScaleOffset.Add(scaleOffset); + atlasOnlySize.Add(lmgroup == null ? 0 : lmgroup.resolution); + atlasOnlyID.Add(lmgroup == null ? 0 : lmgroup.id); + } + yield break; + } + + // Sort LMGroups so vertex groups are never first (because Unity assumes lightmap compression on LM0) + for(int i=0; i<groupList.Count; i++) + { + groupList[i].sortingID = i; + } + groupList.Sort(delegate(BakeryLightmapGroup a, BakeryLightmapGroup b) + { + int aa = (a.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) ? -1 : 1; + int bb = (b.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) ? -1 : 1; + return bb.CompareTo(aa); + }); + var lmBounds2 = new List<Bounds>(); + for(int i=0; i<groupList.Count; i++) + { + lmBounds2.Add(lmBounds[groupList[i].sortingID]); // apply same sorting to lmBounds + groupList[i].id = i; + } + lmBounds = lmBounds2; + + // Check for existing files + if (overwriteWarning) + { + var checkGroupList = groupList; + if (overwriteWarningSelectedOnly) + { + var selObjs = Selection.objects; + checkGroupList = new List<BakeryLightmapGroup>(); + for(int o=0; o<selObjs.Length; o++) + { + if (selObjs[o] as GameObject == null) continue; + var selGroup = GetLMGroupFromObject(selObjs[o] as GameObject, data); + if (selGroup == null) continue; + if (!checkGroupList.Contains(selGroup)) + { + checkGroupList.Add(selGroup); + } + } + } + var existingFilenames = ""; + for(int i=0; i<checkGroupList.Count; i++) + { + var nm = checkGroupList[i].name; + var filename = nm + "_final" + overwriteExtensionCheck; + var outputPath = ftRenderLightmap.outputPathFull; + if (File.Exists("Assets/" + outputPath + "/" + filename)) + { + existingFilenames += filename + "\n"; + } + } + if (existingFilenames.Length > 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<groupList.Count; i++) + { + var lmgroup = groupList[i]; + var res = lmgroup.resolution; + ulong lightingSize = (ulong)(res * res * 4 * 2); // RGBA16f + approxMem += lightingSize; + } + var tileSize = ftRenderLightmap.tileSize; + approxMem += (ulong)(tileSize * tileSize * 16 * 2); // maximum 2xRGBA32f (for fixPos12) + } + + if (memoryWarning && ftRenderLightmap.verbose) + { + ProgressBarEnd(false); + if (!EditorUtility.DisplayDialog("Lightmap memory check", "Rendering may require more than " + (ulong)((approxMem/1024)/1024) + "MB of video memory. Continue?", "Continue", "Cancel")) + { + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + ProgressBarInit("Exporting scene - preparing...", window); + } + + + if (ftRenderLightmap.giLodMode == ftRenderLightmap.GILODMode.Auto) + { + + approxMem /= 1024; + approxMem /= 1024; + approxMem += 1024; // scene geometry size estimation - completely random + + if ((int)approxMem > 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<objsToWrite.Count; i++) + { + var obj = objsToWrite[i]; + if (obj.name != "__ExportTerrain") continue; + + var holderObj = objsToWriteHolder[i]; + Rect rc = new Rect(); + if (holderObj != null) + { + if (!holderRect.TryGetValue(holderObj, out rc)) + { + holderObj = null; + } + } + if (holderObj == null) continue; + + var lmgroup = objsToWriteGroup[i]; + //float terrainPixelWidth = rc.width * lmgroup.resolution; + //float terrainPixelHeight = rc.height * lmgroup.resolution; + + var index = terrainObjectList.IndexOf(obj.transform.parent.gameObject); + var terrain = terrainObjectToActual[index]; + var tdata = terrain.terrainData; + //var heightmapResolution = tdata.heightmapResolution; + + //int closestSize = (int)Mathf.Min(Mathf.NextPowerOfTwo((int)Mathf.Max(terrainPixelWidth, terrainPixelHeight)), heightmapResolution-1); + //if (closestSize < 2) continue; + //int mipLog2 = (int)(Mathf.Log(closestSize) / Mathf.Log(2.0f)); + //int maxMipLog2 = (int)(Mathf.Log(heightmapResolution-1) / Mathf.Log(2.0f)); + //int mip = maxMipLog2 - mipLog2; + + float scaleX = tdata.size.x;// / (heightmapResolution-1); + //float scaleY = tdata.size.y; + float scaleZ = tdata.size.z;// / (heightmapResolution-1); + float offsetX = obj.transform.position.x; + float offsetY = obj.transform.position.y; + float offsetZ = obj.transform.position.z; + + terrainObjectToLMID[index] = lmgroup.id; + terrainObjectToBoundsUV[index*4] = rc.x; + terrainObjectToBoundsUV[index*4+1] = rc.y; + terrainObjectToBoundsUV[index*4+2] = rc.width; + terrainObjectToBoundsUV[index*4+3] = rc.height; + + if (uvgbHeightmap) + { + var indexArrays = objsToWriteIndices[i] = new int[1][]; + var indexArray = indexArrays[0] = new int[6];//(closestSize-1)*(closestSize-1)*6]; + //int indexOffset = 0; + //int vertOffset = 0; + var uvArray = objsToWriteVerticesUV[i] = objsToWriteVerticesUV2[i] = new Vector2[4];//closestSize*closestSize]; + + var posArray = objsToWriteVerticesPosW[i] = new Vector3[4]; + var normalArray = objsToWriteVerticesNormalW[i] = new Vector3[4]; + + posArray[0] = new Vector3(offsetX, offsetY, offsetZ); + posArray[1] = new Vector3(offsetX + scaleX, offsetY, offsetZ); + posArray[2] = new Vector3(offsetX, offsetY, offsetZ + scaleZ); + posArray[3] = new Vector3(offsetX + scaleX, offsetY, offsetZ + scaleZ); + + normalArray[0] = Vector3.up; + normalArray[1] = Vector3.up; + normalArray[2] = Vector3.up; + normalArray[3] = Vector3.up; + + uvArray[0] = new Vector2(0,0); + uvArray[1] = new Vector2(1,0); + uvArray[2] = new Vector2(0,1); + uvArray[3] = new Vector2(1,1); + + indexArray[0] = 0; + indexArray[1] = 2; + indexArray[2] = 3; + + indexArray[3] = 0; + indexArray[4] = 3; + indexArray[5] = 1; + } + else + { + /*if (mip == 0) + { + // use existing heightmap + var heights = tdata.GetHeights(0, 0, heightmapResolution, heightmapResolution); + var posArray = objsToWriteVerticesPosW[i] = new Vector3[heightmapResolution * heightmapResolution]; + objsToWriteVerticesNormalW[i] = terrainObjectToNormalMip0[index]; + closestSize = heightmapResolution; + scaleX /= closestSize-1; + scaleZ /= closestSize-1; + for(int y=0; y<closestSize; y++) + { + for(int x=0; x<closestSize; x++) + { + float px = x * scaleX + offsetX; + float pz = y * scaleZ + offsetZ; + posArray[y * closestSize + x] = new Vector3(px, heights[y, x] * scaleY + offsetY, pz); + } + } + } + else + { + // use mip + var heights = terrainObjectToHeightMips[index][mip - 1]; + var posArray = objsToWriteVerticesPosW[i] = new Vector3[closestSize * closestSize]; + objsToWriteVerticesNormalW[i] = terrainObjectToNormalMips[index][mip - 1]; + scaleX /= closestSize-1; + scaleZ /= closestSize-1; + for(int y=0; y<closestSize; y++) + { + for(int x=0; x<closestSize; x++) + { + float px = x * scaleX + offsetX; + float pz = y * scaleZ + offsetZ; + posArray[y * closestSize + x] = new Vector3(px, heights[y * closestSize + x] * scaleY + offsetY, pz); + } + } + } + var indexArrays = objsToWriteIndices[i] = new int[1][]; + var indexArray = indexArrays[0] = new int[(closestSize-1)*(closestSize-1)*6]; + int indexOffset = 0; + int vertOffset = 0; + var uvArray = objsToWriteVerticesUV[i] = objsToWriteVerticesUV2[i] = new Vector2[closestSize*closestSize]; + for(int y=0; y<closestSize; y++) + { + for(int x=0; x<closestSize; x++) + { + uvArray[y * closestSize + x] = new Vector2(x / (float)(closestSize-1), y / (float)(closestSize-1)); + + if (x < closestSize-1 && y < closestSize-1) + { + indexArray[indexOffset] = vertOffset; + indexArray[indexOffset + 1] = vertOffset + closestSize; + indexArray[indexOffset + 2] = vertOffset + closestSize + 1; + + indexArray[indexOffset + 3] = vertOffset; + indexArray[indexOffset + 4] = vertOffset + closestSize + 1; + indexArray[indexOffset + 5] = vertOffset + 1; + + indexOffset += 6; + } + vertOffset++; + } + }*/ + } + } + + // Export heightmap metadata + if (terrainObjectToActual.Count > 0) + { + for(int i=0; i<terrainObjectToHeightMap.Count; i++) + { + fhmaps.Write(terrainObjectToLMID[i]); + for(int fl=0; fl<6; fl++) fhmaps.Write(terrainObjectToBounds[i*6+fl]); + for(int fl=0; fl<4; fl++) fhmaps.Write(terrainObjectToBoundsUV[i*4+fl]); + fhmaps.Write(terrainObjectToFlags[i]); + } + } + } + + // Write mark last written scene + File.WriteAllText(scenePath + "/lastscene.txt", ftRenderLightmap.GenerateLightingDataAssetName()); + + // Write lightmap definitions + var flms = new BinaryWriter(File.Open(scenePath + "/lms.bin", FileMode.Create)); + var flmlod = new BinaryWriter(File.Open(scenePath + "/lmlod.bin", FileMode.Create)); + var flmuvgb = new BinaryWriter(File.Open(scenePath + "/lmuvgb.bin", FileMode.Create)); + + if (ftRenderLightmap.clientMode) + { + ftClient.serverFileList.Add("lms.bin"); + ftClient.serverFileList.Add("lmlod.bin"); + ftClient.serverFileList.Add("lmuvgb.bin"); + } + + // Init global UVGBuffer flags + int uvgbGlobalFlags = 0; + int _UVGBFLAG_SMOOTHPOS = pstorage.generateSmoothPos ? UVGBFLAG_SMOOTHPOS : 0; + if (exportShaderColors) + { + if (ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.BakedNormalMaps) + { + uvgbGlobalFlags = UVGBFLAG_FACENORMAL | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS; + } + else if (ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.RNM || + (ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.SH && tangentSHLights)) + { + uvgbGlobalFlags = UVGBFLAG_NORMAL | UVGBFLAG_FACENORMAL | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS | UVGBFLAG_TANGENT; + } + else + { + uvgbGlobalFlags = UVGBFLAG_NORMAL | UVGBFLAG_FACENORMAL | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS; + } + } + else + { + uvgbGlobalFlags = UVGBFLAG_NORMAL | UVGBFLAG_FACENORMAL | UVGBFLAG_ALBEDO | UVGBFLAG_EMISSIVE | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS; + } + if (terrainObjectToActual.Count > 0) uvgbGlobalFlags |= UVGBFLAG_TERRAIN; + SetUVGBFlags(uvgbGlobalFlags); + + for(int i=0; i<groupList.Count; i++) + { + var lmgroup = groupList[i]; + flms.Write(lmgroup.name); + + flmlod.Write(lmgroup.sceneLodLevel); + + int uvgbflags = 0; + + if (lmgroup.containsTerrains && exportTerrainAsHeightmap) + uvgbflags = uvgbGlobalFlags | (UVGBFLAG_NORMAL | UVGBFLAG_TERRAIN); + + if (lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.BakedNormalMaps) + uvgbflags = UVGBFLAG_FACENORMAL | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS; + + if (lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.RNM || + (lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.SH && tangentSHLights)) + uvgbflags = UVGBFLAG_NORMAL | UVGBFLAG_FACENORMAL | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS | UVGBFLAG_TANGENT; + + if (lmgroup.probes) uvgbflags = UVGBFLAG_RESERVED; + + flmuvgb.Write(uvgbflags); + + if (ftRenderLightmap.clientMode) + { + if (uvgbflags == 0) uvgbflags = uvgbGlobalFlags; + + ftClient.serverFileList.Add("uvpos_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds")); + ftClient.serverFileList.Add("uvnormal_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds")); + ftClient.serverFileList.Add("uvalbedo_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds")); + if (lmgroup.mode != BakeryLightmapGroup.ftLMGroupMode.Vertex) + { + if ((uvgbflags & UVGBFLAG_SMOOTHPOS) != 0) ftClient.serverFileList.Add("uvsmoothpos_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds")); + } + if ((uvgbflags & UVGBFLAG_FACENORMAL) != 0) ftClient.serverFileList.Add("uvfacenormal_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds")); + if ((uvgbflags & UVGBFLAG_TANGENT) != 0) ftClient.serverFileList.Add("uvtangent_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds")); + } + + if (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) + { + int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)lmgroup.totalVertexCount)); + if (atlasTexSize > 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<ushort>(); + + int albedoCounter = 0; + var albedoMap = new Dictionary<IntPtr, int>(); // albedo ptr -> ID map + + int alphaCounter = 0; + var alphaMap = new Dictionary<IntPtr, List<int>>(); // alpha ptr -> ID map + + var dummyTexList = new List<Texture>(); // 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<MeshFilter>().sharedMesh; + var pmesh = plane.GetComponent<MeshFilter>().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<groupList.Count; g++) + { + var lmgroup = groupList[g]; + if (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) continue; + for(int i=0; i<objsToWrite.Count; i++) + { + if (objsToWriteGroup[i] != lmgroup) continue; + var obj = objsToWrite[i]; + + var mesh = GetSharedMesh(obj); + if (mesh == qmesh || mesh == pmesh) continue; + + var uv = objsToWriteVerticesUV[i];//mesh.uv; + var uv2 = objsToWriteVerticesUV2[i];//mesh.uv2; + var usedUVs = uv2.Length == 0 ? uv : uv2; + bool validUVs = true; + for(int v=0; v<usedUVs.Length; v++) + { + if (usedUVs[v].x < -0.0001f || usedUVs[v].x > 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<sceneLodsUsed; i++) + { + fib32lod[i] = new BinaryWriter(File.Open(scenePath + "/ib32_lod" + i + ".bin", FileMode.Create)); + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add("ib32_lod" + i + ".bin"); + } + falphaidlod = new BinaryWriter[sceneLodsUsed]; + for(int i=0; i<sceneLodsUsed; i++) + { + falphaidlod[i] = new BinaryWriter(File.Open(scenePath + "/alphaid_lod" + i + ".bin", FileMode.Create)); + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add("alphaid2_lod" + i + ".bin"); // alphaid2, not alphaid + } + + if (ftRenderLightmap.clientMode) + { + ftClient.serverFileList.Add("objects.bin"); + ftClient.serverFileList.Add("mesh.bin"); + ftClient.serverFileList.Add("lmid.bin"); + ftClient.serverFileList.Add("seamfix.bin"); + ftClient.serverFileList.Add("surf.bin"); + ftClient.serverFileList.Add("matid.bin"); + ftClient.serverFileList.Add("emissiveid.bin"); + ftClient.serverFileList.Add("emissivemul.bin"); + ftClient.serverFileList.Add("heightmapid.bin"); + ftClient.serverFileList.Add("alphaid2.bin"); // alphaid2, not alphaid + ftClient.serverFileList.Add("alphabuffer.bin"); + ftClient.serverFileList.Add("vbfull.bin"); + ftClient.serverFileList.Add("vbtrace.bin"); + ftClient.serverFileList.Add("vbtraceTex.bin"); + ftClient.serverFileList.Add("vbtraceUV0.bin"); + ftClient.serverFileList.Add("ib.bin"); + ftClient.serverFileList.Add("ib32.bin"); + } + + // Export heightmap metadata + //fhmaps.Write(terrainObjectToActual.Count); + if (terrainObjectToActual.Count > 0) + { + //terrainObjectToHeightMapPtr = new IntPtr[terrainObjectToHeightMap.Count]; + /*for(int i=0; i<terrainObjectToHeightMap.Count; i++) + { + for(int fl=0; fl<6; fl++) fhmaps.Write(terrainObjectToBounds[i*6+fl]); + }*/ + } + + // Export some scene data + // - LMIDs + // - mesh definitions + // - surface definitions + // - albedo IDs + // - alpha IDs + // - update LMGroup bounds + // - export index buffer + // - generate tracing index buffer + + areaLightCounter = -2; + //var defaultTexST = new Vector4(1,1,0,0); + //var objsToWriteTexST = new List<Vector4>(); + + for(int i=0; i<objsToWrite.Count; i++) + { + var obj = objsToWrite[i]; + var lmgroup = objsToWriteGroup[i]; + var holderObj = objsToWriteHolder[i]; + + if (obj == null) + { + // wtf + DebugLogError("Object " + objsToWriteNames[i] + " was destroyed mid-export"); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + + var mr = GetValidRenderer(obj); + var m = GetSharedMesh(mr); + + var inds = objsToWriteIndices[i]; + + // Write LMID, mesh and surface definition + int id = exportLMID(flmid, obj, lmgroup); + exportMesh(fmesh, m); + exportSurfs(fsurf, inds, inds.Length);// m); + + int lodLevel; + if (!objToLodLevel.TryGetValue(obj, out lodLevel)) lodLevel = -1; + + bool isTerrain = (exportTerrainAsHeightmap && obj.name == "__ExportTerrain"); + bool hasMetaAlpha = false; + + // Write albedo IDs, collect alpha IDs, update LMGroup bounds + if (id >= 0) { + for(int k=0; k<m.subMeshCount; k++) { + // Get mesh albedos + int texID = -1; + Material mat = null; + Texture tex = null; + //var texST = defaultTexST; + if (k < mr.sharedMaterials.Length) { + mat = mr.sharedMaterials[k]; + if (mat != null) + { + if (mat.HasProperty("_MainTex")) + { + tex = mat.mainTexture; + //if (mat.HasProperty("_MainTex_ST")) + //{ + // texST = mat.GetVector("_MainTex_ST"); + //} + } + else if (mat.HasProperty("_BaseColorMap")) + { + // HDRP + tex = mat.GetTexture("_BaseColorMap"); + } + else if (mat.HasProperty("_BaseMap")) + { + // URP + tex = mat.GetTexture("_BaseMap"); + } + } + } + IntPtr texPtr = (IntPtr)0; + Texture texWrite = null; + if (tex != null) + { + texPtr = tex.GetNativeTexturePtr(); + texWrite = tex; + if (texPtr == (IntPtr)0) + { + Debug.LogError("Texture " + tex.name + " cannot be used as albedo (GetNativeTexturePtr returned null)"); + tex = null; + texWrite = null; + } + else if ((tex as RenderTexture) != null) + { + Debug.LogError("RenderTexture " + tex.name + " cannot be used as albedo (GetNativeTexturePtr returned null)"); + tex = null; + texWrite = null; + } + } + if (tex == null) + { + // Create dummy 1px texture + var dummyTex = new Texture2D(1,1); + dummyPixelArray[0] = (mat == null || !mat.HasProperty("_Color")) ? Color.white : mat.color; + dummyTex.SetPixels(dummyPixelArray); + dummyTex.Apply(); + texWrite = dummyTex; + dummyTexList.Add(dummyTex); + texPtr = dummyTex.GetNativeTexturePtr(); + if (texPtr == (IntPtr)0) + { + Debug.LogError("Failed to call GetNativeTexturePtr() on newly created texture"); + texWrite = null; + } + } + if (!albedoMap.TryGetValue(texPtr, out texID)) + { + lmAlbedoList.Add(texPtr); + lmAlbedoListTex.Add(texWrite); + albedoMap[texPtr] = albedoCounter; + texID = albedoCounter; + albedoCounter++; + } + + // Write albedo ID + fmatid.Write((ushort)texID); + + // Get mesh alphas + ushort alphaID = 0xFFFF; + int alphaChannel = 3; // A + if (mat != null && mat.HasProperty("_TransparencyLM")) // will override _MainTex.a if present + { + var tex2 = mat.GetTexture("_TransparencyLM"); + if (tex2 != null) + { + tex = tex2; + texPtr = tex.GetNativeTexturePtr(); + alphaChannel = 0; // R + } + } + bool alphaMetaPass = (mat != null && mat.HasProperty("BAKERY_META_ALPHA_ENABLE")); + if (alphaMetaPass && ftRenderLightmap.lightProbeMode == ftRenderLightmap.LightProbeMode.Legacy) + { + alphaMetaPass = false; + Debug.LogError("Skipping alpha meta pass in legacy light probe mode"); + } + if (tex != null || alphaMetaPass) { + var matTag = mat.GetTag("RenderType", true); + bool isCutout = matTag == "TransparentCutout"; + if (isCutout || matTag == "Transparent" || matTag == "TreeLeaf") { + + float alphaRef = 0.5f; + if (mat != null && mat.HasProperty("_Cutoff")) + { + alphaRef = mat.GetFloat("_Cutoff"); + } + float opacity = 1.0f; + if (!isCutout && mat.HasProperty("_Color")) + { + opacity = mat.color.a; + } + // let constant alpha affect cutout theshold for alphablend materials + alphaRef = 1.0f - (1.0f - alphaRef) * opacity; + if (alphaRef > 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<int> texIDs; + if (!alphaMap.TryGetValue(texPtr, out texIDs)) + { + alphaMap[texPtr] = texIDs = new List<int>(); + + 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<texIDs.Count; instance++) + { + texID = texIDs[instance]; + if (Mathf.Abs(lmAlphaRefList[texID] - alphaRef) <= alphaInstanceThreshold) + { + if (lmAlphaChannelList[texID] == alphaChannel) + { + matchingInstance = instance; + alphaID = (ushort)texID; + break; + } + } + } + if (matchingInstance < 0) + { + 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; + } + } + } + } + } + alphaIDs.Add(alphaID); + + // Get mesh emissives + if (exportShaderColors) + { + for(int s=0; s<sceneCount; s++) + { + if (storages[s] == null) continue; + while(storages[s].hasEmissive.Count <= id) storages[s].hasEmissive.Add(true); + storages[s].hasEmissive[id] = true; + } + } + + texID = -1; + tex = null; + if (mat!=null && mat.shaderKeywords.Contains("_EMISSION")) + { + if (mat.HasProperty("_EmissionMap")) tex = mat.GetTexture("_EmissionMap"); + if (tex != null) + { + texPtr = tex.GetNativeTexturePtr(); + if (texPtr == (IntPtr)0) + { + if ((tex as RenderTexture) != null) + { + Debug.LogError("RenderTexture " + tex.name + " cannot be used as emission (GetNativeTexturePtr returned null)"); + tex = null; + } + else + { + Debug.LogError("Texture " + tex.name + " cannot be used as emission (GetNativeTexturePtr returned null)"); + tex = null; + } + //DebugLogError("Null emission tex ptr"); + } + } + if (tex == null && mat.HasProperty("_EmissionColor")) + { + // Create dummy 1px texture + var dummyTex = new Texture2D(1,1); + dummyPixelArray[0] = mat.GetColor("_EmissionColor"); + dummyTex.SetPixels(dummyPixelArray); + dummyTex.Apply(); + tex = dummyTex; + dummyTexList.Add(dummyTex); + texPtr = dummyTex.GetNativeTexturePtr(); + if (texPtr == (IntPtr)0) + { + Debug.LogError("Failed to call GetNativeTexturePtr() on newly created texture"); + texWrite = null; + //DebugLogError("Null dummy tex ptr"); + } + } + if (!albedoMap.TryGetValue(texPtr, out texID)) + { + lmAlbedoList.Add(texPtr); + lmAlbedoListTex.Add(tex); + albedoMap[texPtr] = albedoCounter; + texID = albedoCounter; + albedoCounter++; + } + for(int s=0; s<sceneCount; s++) + { + if (storages[s] == null) continue; + while(storages[s].hasEmissive.Count <= id) storages[s].hasEmissive.Add(false); + storages[s].hasEmissive[id] = true; + } + + fmatide.Write((ushort)texID); + fmatideb.Write(mat.HasProperty("_EmissionColor") ? mat.GetColor("_EmissionColor").maxColorComponent : 1); + } + else + { + fmatide.Write((ushort)0xFFFF); + fmatideb.Write(0.0f); + } + + if (isTerrain && uvgbHeightmap) + { + var hindex = terrainObjectList.IndexOf(obj.transform.parent.gameObject); + //var htex = terrainObjectToHeightMap[hindex]; + //texPtr = htex.GetNativeTexturePtr(); + + //heightmapList.Add(texPtr); + //heightmapListTex.Add(htex); + //heightmapListBounds.Add(); + + //texID = heightmapCounter; + //heightmapCounter++; + + fmatidh.Write((ushort)hindex);//texID); + } + else + { + fmatidh.Write((ushort)0xFFFF); + } + } + + // Update LMGroup bounds + if (modifyLightmapStorage) + { + if (lmBounds[id].size == Vector3.zero) { + lmBounds[id] = mr.bounds; + } else { + var b = lmBounds[id]; + b.Encapsulate(mr.bounds); + lmBounds[id] = b; + } + + if (isTerrain && exportTerrainAsHeightmap) + { + var b = lmBounds[id]; + var hindex = terrainObjectList.IndexOf(obj.transform.parent.gameObject); + var terr = terrainObjectToActual[hindex]; + var tb = terr.terrainData.bounds; + tb.center += terr.transform.position; + b.Encapsulate(tb); + lmBounds[id] = b; + } + } + + } else { + // Write empty albedo/alpha IDs for non-lightmapped + for(int k=0; k<m.subMeshCount; k++) { + fmatid.Write((ushort)0); + alphaIDs.Add(0xFFFF); + fmatide.Write((ushort)0xFFFF); + fmatideb.Write(0.0f); + fmatidh.Write((ushort)0xFFFF); + } + } + + // Mark whole mesh that some submeshes use Meta Pass alpha + objsToWriteHasMetaAlpha.Add(hasMetaAlpha); + + int currentVoffset = voffset; + voffset += objsToWriteVerticesPosW[i].Length;// m.vertexCount; + + // Check if mesh is flipped + var ls = obj.transform.lossyScale; + bool isFlipped = Mathf.Sign(ls.x*ls.y*ls.z) < 0; + if (lmgroup != null && lmgroup.flipNormal) isFlipped = !isFlipped; + + while(lmIndexArrays.Count <= id) + { + lmIndexArrays.Add(new List<int>()); + lmLocalToGlobalIndices.Add(new List<int>()); + 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<m.subMeshCount;k++) { + // Export regular index buffer + //var indexCount = exportIB(fib, m, k, isFlipped, false, 0, null, 0); + var indexCount = exportIB(fib, inds[k], isFlipped, false, 0, null, 0); + + bool submeshCastsShadows = castsShadows; + if (submeshCastsShadows) + { + var mats = mmr.sharedMaterials; + if (mats.Length > 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<visList.Count; vlod++) + { + int lod = visList[vlod]; + var indicesOpaqueArray = data.indicesOpaqueLOD[lod]; + var indicesTransparentArray = data.indicesTransparentLOD[lod]; + var falphaidFile = falphaidlod[lod]; + exportIB32(indicesOpaqueArray, indicesTransparentArray, id>=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<vcount; k++) + { + remapArray.Add(k + currentVoffset); + } + lmVOffset[id] += vcount; + } + } + + } + catch(Exception e) + { + DebugLogError("Error exporting scene - see console for details"); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + Debug.LogError("Exception caught: " + e.ToString()); + throw; + } + + ProgressBarShow("Exporting scene - finishing objects...", 0.5f, false); + if (userCanceled) + { + CloseAllFiles(); + ProgressBarEnd(true); + yield break; + } + yield return null; + + try + { + // Write vertex buffers and update storage + Rect rc = new Rect(); + var emptyVec4 = new Vector4(1,1,0,0); + for(int i=0; i<objsToWrite.Count; i++) + { + var obj = objsToWrite[i]; + var m = GetSharedMesh(obj); + var lmgroup = objsToWriteGroup[i]; + + var id = lmgroup == null ? -1 : objsToWriteGroup[i].id; + + BakeryLightMesh areaLight = obj.GetComponent<BakeryLightMesh>(); + 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<Renderer>().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<tformedPos.Length; t++) + { + if (holderObj != null) + { + tformedUV2[t].x = uv2[t].x * rc.width + rc.x; + tformedUV2[t].y = uv2[t].y * rc.height + rc.y; + } + } + objsToWriteUVOverride.Add(null); + } + else if (vertexBake) + { + tformedUV2 = GenerateVertexBakeUVs(lmgroup.vertexCounter, tformedPos.Length, lmgroup.totalVertexCount); + lmgroup.vertexCounter += tformedPos.Length; + objsToWriteUVOverride.Add(tformedUV2); + } + else + { + tformedUV2 = uv; + objsToWriteUVOverride.Add(null); + } + + if (id >= 0) + { + while(lmUVArrays.Count <= id) + { + lmUVArrays.Add(new List<float>()); + } + var lmUVArray = lmUVArrays[id]; + for(int k=0; k<tformedUV2.Length; k++) + { + lmUVArray.Add(tformedUV2[k].x); + lmUVArray.Add(tformedUV2[k].y); + } + } + + if (objsToWriteHasMetaAlpha[i]) uv = tformedUV2; // objects using Meta Pass alpha use UV2 instead of UV1 + + exportVBFull(fvbfull, tformedPos, tformedNormals, tformedTangents, uv, tformedUV2); + vbTimeWriteFull += GetTime() - time; + time = GetTime(); + //if (castsShadows) + //{ + exportVBTrace(fvbtrace, m, tformedPos, tformedNormals); + vbTimeWriteT += GetTime() - time; + time = GetTime(); + exportVBTraceTexAttribs(vbtraceTexPosNormalArray, vbtraceTexUVArray, tformedPos, tformedNormals, tformedUV2, id, vertexBake, obj); + vbTimeWriteT2 += GetTime() - time; + time = GetTime(); + exportVBTraceUV0(fvbtraceUV0, uv, tformedPos.Length); + vbTimeWriteT3 += GetTime() - time; + time = GetTime(); + //} + voffset += tformedPos.Length; + vbTimeWrite += GetTime() - time2; + + + // update storage + // also write seamfix.bin + var sceneID = sceneToID[obj.scene]; + if (obj.name == "__ExportTerrain") + { + fseamfix.Write(false); + var index = terrainObjectList.IndexOf(obj.transform.parent.gameObject); + var terrain = terrainObjectToActual[index]; + var scaleOffset = holderObj == null ? emptyVec4 : new Vector4(rc.width, rc.height, rc.x, rc.y); + if (!storages[sceneID].bakedRenderersTerrain.Contains(terrain)) + { + if (modifyLightmapStorage) + { + storages[sceneID].bakedRenderersTerrain.Add(terrain); + storages[sceneID].bakedIDsTerrain.Add(CorrectLMGroupID(id, lmgroup, groupList)); + storages[sceneID].bakedScaleOffsetTerrain.Add(scaleOffset); + } + } + objsToWriteScaleOffset.Add(scaleOffset); + } + else + { + fseamfix.Write(true); + var scaleOffset = holderObj == null ? emptyVec4 : new Vector4(rc.width, rc.height, rc.x, rc.y); + if (modifyLightmapStorage) + { + bool vertexImplicit = false; + if (vertexBake) + { + if (lmgroup.isImplicit) vertexImplicit = true; + } + if (!vertexImplicit) + { + storages[sceneID].bakedRenderers.Add(GetValidRenderer(obj)); + storages[sceneID].bakedIDs.Add(CorrectLMGroupID(id, lmgroup, groupList)); + storages[sceneID].bakedScaleOffset.Add(scaleOffset); + storages[sceneID].bakedVertexOffset.Add(vertexBake ? (lmgroup.vertexCounter - tformedPos.Length) : -1); + storages[sceneID].bakedVertexColorMesh.Add(null); + } + } + objsToWriteScaleOffset.Add(scaleOffset); + } + } + + // Generate LOD UVs + if (ftRenderLightmap.giLodModeEnabled) + { + for(int s=0; s<sceneCount; s++) + { + if (storages[s] == null) continue; + storages[s].lmGroupMinLOD = new int[groupList.Count]; + storages[s].lmGroupLODMatrix = new int[groupList.Count * groupList.Count]; + } + for(int i=0; i<groupList.Count; i++) + { + var lmgroup = groupList[i]; + if (lmgroup.resolution < 128) + { + lmUVArrays2.Add(null); + lmIndexArrays2.Add(null); + lmUVArrays3.Add(null); + continue; + } + if (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex || lmgroup.containsTerrains) + { + lmUVArrays2.Add(null); + lmIndexArrays2.Add(null); + lmUVArrays3.Add(null); + if (lmgroup.containsTerrains) + { + int minLodResolutionTerrain = 128; + for(int s=0; s<sceneCount; s++) + { + if (storages[s] == null) continue; + int minLOD = (int)(Mathf.Log(lmgroup.resolution, 2.0f) - Mathf.Log(minLodResolutionTerrain, 2.0f)) - 1; + if (minLOD < 0) minLOD = 0; + storages[s].lmGroupMinLOD[lmgroup.id] = minLOD; + } + } + continue; + } + int id = lmgroup.id; + lmUVArrays2.Add(lmUVArrays[i].ToArray()); + lmIndexArrays2.Add(lmIndexArrays[i].ToArray()); + + lmUVArrays3.Add(lmUVArrays[i].ToArray()); + int uvIslands = uvrLoad(lmUVArrays2[i], lmUVArrays2[i].Length/2, lmIndexArrays2[i], lmIndexArrays2[i].Length); + if (uvIslands <= 0) + { + Debug.LogError("Can't generate LOD UVs for " + lmgroup.name+" "+lmUVArrays2[i].Length+" "+lmIndexArrays2[i].Length+" "+lmgroup.containsTerrains); + uvrUnload(); + continue; + } + int minLodResolution = Mathf.NextPowerOfTwo((int)Mathf.Ceil(Mathf.Sqrt((float)uvIslands))); + minLodResolution = minLodResolution << 1; + if (minLodResolution > 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<sceneCount; s++) + { + if (storages[s] == null) continue; + int minLOD = (int)(Mathf.Log(lmgroup.resolution, 2.0f) - Mathf.Log(minLodResolution, 2.0f)) - 1; + if (minLOD < 0) minLOD = 0; + storages[s].lmGroupMinLOD[lmgroup.id] = minLOD; + } + + int uvrErrCode = uvrRepack(0, minLodResolution); + if (uvrErrCode == -1) + { + Debug.LogError("Can't repack LOD UVs for " + lmgroup.name); + uvrUnload(); + continue; + } + DebugLogInfo("Tries left: " + uvrErrCode); + uvrUnload(); + var numLocalVerts = lmUVArrays2[i].Length / 2; + for(int k=0; k<numLocalVerts; k++) + { + float u = lmUVArrays2[i][k * 2]; + u = Mathf.Clamp(u, 0, 0.99999f); + u += id * 10; + if (i >= 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<numTraceVerts; i++) + { + fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6]); + fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6 + 1]); + fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6 + 2]); + fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6 + 3]); + fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6 + 4]); + fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6 + 5]); + + fvbtraceTex.Write(vbtraceTexUVArray[i * 2]); + fvbtraceTex.Write(vbtraceTexUVArray[i * 2 + 1]); + } + + // Generate LOD UV buffer + if (ftRenderLightmap.giLodModeEnabled) + { + var uvBuffOffsets = new int[lmUVArrays3.Count]; + var uvBuffLengths = new int[lmUVArrays3.Count]; + int uvBuffSize = 0; + for(int i=0; i< lmUVArrays3.Count; i++) + { + if (lmUVArrays3[i] == null) continue; + uvBuffOffsets[i] = uvBuffSize; + uvBuffLengths[i] = lmUVArrays3[i].Length; + uvBuffSize += lmUVArrays3[i].Length; + } + var uvSrcBuff = new float[uvBuffSize]; + var uvDestBuff = new float[uvBuffSize]; + for(int i=0; i< lmUVArrays3.Count; i++) + { + if (lmUVArrays3[i] == null) continue; + var arr = lmUVArrays3[i]; + var arr2 = lmUVArrays2[i]; + var offset = uvBuffOffsets[i]; + for(int j=0; j<arr.Length; j++) + { + uvSrcBuff[j + offset] = arr[j]; + uvDestBuff[j + offset] = arr2[j]; + } + } + var lmrIndicesOffsets = new int[lmIndexArrays2.Count]; + var lmrIndicesLengths = new int[lmIndexArrays2.Count]; + int lmrIndicesSize = 0; + for(int i=0; i< lmIndexArrays2.Count; i++) + { + if (lmIndexArrays2[i] == null) continue; + lmrIndicesOffsets[i] = lmrIndicesSize; + lmrIndicesLengths[i] = lmIndexArrays2[i].Length; + lmrIndicesSize += lmIndexArrays2[i].Length; + } + var lmrIndicesBuff = new int[lmrIndicesSize]; + for(int i=0; i< lmIndexArrays2.Count; i++) + { + if (lmIndexArrays2[i] == null) continue; + var arr = lmIndexArrays2[i]; + var offset = lmrIndicesOffsets[i]; + for(int j=0; j<arr.Length; j++) + { + lmrIndicesBuff[j + offset] = arr[j]; + } + } + + for(int s=0; s<sceneCount; s++) + { + if (storages[s] == null) continue; + storages[s].uvBuffOffsets = uvBuffOffsets; + storages[s].uvBuffLengths = uvBuffLengths; + storages[s].uvSrcBuff = uvSrcBuff; + storages[s].uvDestBuff = uvDestBuff; + storages[s].lmrIndicesOffsets = lmrIndicesOffsets; + storages[s].lmrIndicesLengths = lmrIndicesLengths; + storages[s].lmrIndicesBuff = lmrIndicesBuff; + + } + vbtraceTexUVArrayLOD = new float[vbtraceTexUVArray.Count]; + for(int i=0; i<groupList.Count; i++) + { + var lmgroup = groupList[i]; + if (lmgroup.resolution < 128) continue; + if (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex || lmgroup.containsTerrains) continue; + var remapArray = lmLocalToGlobalIndices[i]; + var uvArray = lmUVArrays2[i]; + for(int j=0; j<remapArray.Count; j++) + { + vbtraceTexUVArrayLOD[remapArray[j]*2] = uvArray[j*2]; + vbtraceTexUVArrayLOD[remapArray[j]*2+1] = uvArray[j*2+1]; + } + } + } + + // Write tracing index buffer + fib32.Write(indicesOpaque.Count); // firstAlphaTriangle + for(int i=0; i<indicesOpaque.Count; i++) fib32.Write(indicesOpaque[i]); // opaque triangles + for(int i=0; i<indicesTransparent.Count; i++) fib32.Write(indicesTransparent[i]); // alpha triangles + + // Write scene LOD tracing index buffers + for(int lod=0; lod<sceneLodsUsed; lod++) + { + var indicesOpaqueArray = data.indicesOpaqueLOD[lod]; + var indicesTransparentArray = data.indicesTransparentLOD[lod]; + fib32lod[lod].Write(indicesOpaqueArray.Count); + for(int i=0; i<indicesOpaqueArray.Count; i++) fib32lod[lod].Write(indicesOpaqueArray[i]); // opaque triangles + for(int i=0; i<indicesTransparentArray.Count; i++) fib32lod[lod].Write(indicesTransparentArray[i]); // alpha triangles + } + + DebugLogInfo("Wrote binaries in " + ((GetTime() - totalTime)/1000.0) + "s"); + DebugLogInfo("VB read time " + (vbTimeRead/1000.0) + "s"); + DebugLogInfo("VB write time " + (vbTimeWrite/1000.0) + "s"); + DebugLogInfo("VB write time (full) " + (vbTimeWriteFull/1000.0) + "s"); + DebugLogInfo("VB write time (trace) " + (vbTimeWriteT/1000.0) + "s"); + DebugLogInfo("VB write time (trace tex) " + (vbTimeWriteT2/1000.0) + "s"); + DebugLogInfo("VB write time (UV0) " + (vbTimeWriteT3/1000.0) + "s"); + DebugLogInfo("IB time " + (ibTime/1000.0) + "s"); + + + fscene.Write(objsToWrite.Count); + int meshID = 0; + foreach(var obj in objsToWrite) { + fscene.Write(meshID); + meshID++; + } + foreach(var obj in objsToWrite) { + fscene.Write(obj.name); + } + + fscene.Close(); + fmesh.Close(); + flmid.Close(); + fseamfix.Close(); + fsurf.Close(); + fmatid.Close(); + fmatide.Close(); + fmatideb.Close(); + fmatidh.Close(); + fvbfull.Close(); + fvbtrace.Close(); + fvbtraceTex.Close(); + fvbtraceUV0.Close(); + fib.Close(); + fib32.Close(); + falphaid.Close(); + fhmaps.Close(); + + if (fib32lod != null) + { + for(int i=0; i<fib32lod.Length; i++) fib32lod[i].Close(); + } + if (falphaidlod != null) + { + for(int i=0; i<falphaidlod.Length; i++) falphaidlod[i].Close(); + } + + if (modifyLightmapStorage) + { + for(int s=0; s<sceneCount; s++) + { + if (storages[s] == null) continue; + storages[s].bounds = lmBounds; + } + } + //} + + startMsU = GetTime(); + } + catch(Exception e) + { + DebugLogError("Error exporting scene - see console for details"); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + Debug.LogError("Exception caught: " + e.ToString()); + throw; + } + + if (exportShaderColors && renderTextures) + { + yield return null; + ProgressBarShow("Exporting scene - shaded surface colors...", 0.55f, false); + for(int g=0; g<groupList.Count; g++) + { + var str = storages[data.firstNonNullStorage]; + if (str == null) Debug.LogError("storages[data.firstNonNullStorage] == null"); + if (str.hasEmissive == null) Debug.LogError("storages[data.firstNonNullStorage].hasEmissive == null"); + if (groupList[g] == null) Debug.LogError("group is null"); + + var hasEmissive = str.hasEmissive.Count > 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<objsToWrite.Count; i++) + { + var obj = objsToWrite[i]; + var lmgroup = objsToWriteGroup[i]; + if (lmgroup == null) continue; + if (lmgroup.id != groupList[g].id) continue; + if (obj.GetComponent<BakeryLightMesh>()) 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<lmAlphaListTex.Count; i++) + { + if (lmAlphaListTex[i] == null) // must be meta alpha + { + int alphaLMID = lmAlphaChannelList[i]; + if (alphaLMID != groupList[g].id) continue; // must link to the same LMID + + // Patch with the new alpha texture + lmAlphaListTex[i] = alpha; + lmAlphaList[i] = alpha.GetNativeTexturePtr(); + if (nonDX11) lmAlphaListRAM[i] = InputDataFromTex(alpha); + lmAlphaChannelList[i] = 3; + } + } + } + } + } + + ProgressBarShow(exportShaderColors ? "Exporting scene - alpha buffer..." : "Exporting scene - UV GBuffer and alpha buffer...", 0.55f, false); + if (userCanceled) + { + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + yield return null; + + InitShaders(); + LoadScene(scenePath); + + // Force load textures to VRAM + var forceRt = new RenderTexture(1, 1, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); + var forceTex = new Texture2D(1, 1, TextureFormat.ARGB32, false, false); + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11) + { + if (!exportShaderColors) + { + for(int i=0; i<lmAlbedoListTex.Count; i++) + { + Graphics.Blit(lmAlbedoListTex[i] as Texture2D, forceRt); + Graphics.SetRenderTarget(forceRt); + forceTex.ReadPixels(new Rect(0,0,1, 1), 0, 0, true); + forceTex.Apply(); + lmAlbedoList[i] = lmAlbedoListTex[i].GetNativeTexturePtr(); + } + } + for(int i=0; i<lmAlphaListTex.Count; i++) + { + Graphics.Blit(lmAlphaListTex[i] as Texture2D, forceRt); + Graphics.SetRenderTarget(forceRt); + forceTex.ReadPixels(new Rect(0,0,1, 1), 0, 0, true); + forceTex.Apply(); + lmAlphaList[i] = lmAlphaListTex[i].GetNativeTexturePtr(); + } + } + + if (exportShaderColors) + { + if (terrainObjectToActual.Count > 0) + { + if (isDX11) + { + var terrainObjectToHeightMapPtr = new IntPtr[terrainObjectToHeightMap.Count]; + for(int i=0; i<terrainObjectToHeightMap.Count; i++) + { + Graphics.Blit(terrainObjectToHeightMap[i] as Texture2D, forceRt); + Graphics.SetRenderTarget(forceRt); + forceTex.ReadPixels(new Rect(0,0,1, 1), 0, 0, true); + forceTex.Apply(); + terrainObjectToHeightMapPtr[i] = terrainObjectToHeightMap[i].GetNativeTexturePtr(); + } + SetAlbedos(terrainObjectToHeightMap.Count, terrainObjectToHeightMapPtr); + int cerr = CopyAlbedos(); + if (cerr != 0) + { + DebugLogError("Failed to copy textures (" + cerr + ")"); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + } + else + { + int cerr = CopyHeightmapsFromRAM(terrainObjectToHeightMapRAM.Count, terrainObjectToHeightMapRAM.ToArray()); + if (cerr != 0) + { + DebugLogError("Failed to copy textures from RAM (" + cerr + ")"); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + } + } + else + { + SetAlbedos(0, null); + } + } + else + { + SetAlbedos(lmAlbedoList.Count, lmAlbedoList.ToArray()); + } + + var lmAlphaListRAMHandle = new GCHandle(); + if (isDX11) + { + // Pass texture objects directly + SetAlphas(lmAlphaList.Count, lmAlphaList.ToArray(), lmAlphaRefList.ToArray(), lmAlphaChannelList.ToArray(), sceneLodsUsed, flipAlpha); + } + else + { + // Pass via RAM + var lmAlphaListRAMArray = lmAlphaListRAM.ToArray(); + lmAlphaListRAMHandle = GCHandle.Alloc(lmAlphaListRAMArray, GCHandleType.Pinned); + SetAlphasFromRAM(lmAlphaListRAM.Count, lmAlphaListRAMHandle.AddrOfPinnedObject(), lmAlphaRefList.ToArray(), lmAlphaChannelList.ToArray(), sceneLodsUsed, flipAlpha); + } + + GL.IssuePluginEvent(6); // render alpha buffer + int uerr = 0; + while(uerr == 0) + { + uerr = GetABGErrorCode(); + yield return null; + } + + if (nonDX11) lmAlphaListRAMHandle.Free(); + + /*yield return new WaitForEndOfFrame(); + int uerr = ftGenerateAlphaBuffer();*/ + if (uerr != 0 && uerr != 99999) + { + DebugLogError("ftGenerateAlphaBuffer error: " + uerr); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + + DestroyImmediate(forceTex); + forceRt.Release(); + + if (!renderTextures) + { + ProgressBarEnd(true);//false); + yield break; + } + + //ProgressBarShow("Exporting scene - UV GBuffer...", 0.8f); + //if (userCanceled) yield break; + //yield return null; + + //GL.IssuePluginEvent(1); // render UV GBuffer + //yield return new WaitForEndOfFrame(); + + SetFixPos(false);//true); // do it manually + SetCompression(ftRenderLightmap.compressedGBuffer); + + if (!exportShaderColors) + { + uerr = ftRenderUVGBuffer(); + if (uerr != 0) + { + DebugLogError("ftRenderUVGBuffer error: " + uerr); + CloseAllFiles(); + userCanceled = true; + ProgressBarEnd(true); + yield break; + } + } + + if (!pstorage.generateSmoothPos) + { + // further passes still require smooth pos, copy from pos + for(int i=0; i<groupList.Count; i++) + { + var lmgroup = groupList[i]; + File.Copy(scenePath + "/uvpos_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), + scenePath + "/uvsmoothpos_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), + true); + } + } + + ms = GetTime(); + DebugLogInfo("UVGB/fixPos/alpha time: " + ((ms - startMsU) / 1000.0) + " seconds"); + + ProgressBarEnd(true); + + DebugLogInfo("Scene export finished"); + } + + int countChildrenFlat(Transform tform) + { + int count = 1; + foreach(Transform t in tform) + { + count += countChildrenFlat(t); + } + return count; + } +} + +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildGraphics.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildGraphics.cs.meta new file mode 100644 index 00000000..65922ed3 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildGraphics.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1f3ae15d674398b46a90bf2c1aced7ac +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildLights.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildLights.cs new file mode 100644 index 00000000..844b1919 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftBuildLights.cs @@ -0,0 +1,1118 @@ +#if UNITY_EDITOR + +// Disable 'obsolete' warnings +#pragma warning disable 0618 + +#define SRGBCONVERT +//#define OPTIMIZEDAREA // BSP +#define OPTIMIZEDAREA2 // efficient weighted sampling +#define LAUNCH_VIA_DLL + +using UnityEngine; +using UnityEditor; +using System.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEngine.Rendering; +using System.Reflection; + +public class ftBuildLights +{ + static Dictionary<UnityEngine.Object, int> tex2hash; + static Dictionary<string, bool> lightSaved; + static bool allowOverwrite = false; + + static System.Type texUtil; + static MethodInfo texUtil_GetUsage; + + static public void InitMaps(bool overwrite) + { + allowOverwrite = overwrite; + tex2hash = new Dictionary<UnityEngine.Object, int>(); + lightSaved = new Dictionary<string, bool>(); + } + + 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<int> 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<int>(); + 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<Vector3> 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<tris; j++) + { + var v1 = verts[indices[j*3]]; + var v2 = verts[indices[j*3 + 1]]; + var v3 = verts[indices[j*3 + 2]]; + + if (corners == null) + { + v1 = tform.TransformPoint(v1); + v2 = tform.TransformPoint(v2); + v3 = tform.TransformPoint(v3); + } + +#if (OPTIMIZEDAREA || OPTIMIZEDAREA2) + area[j] = Vector3.Cross(v2 - v1, v3 - v1).magnitude; + if (area[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<tris; j++) + { + area[j] *= invTotalArea * 0.5f; + sumWeights += area[j]; + } + + float sampleWidth = sumWeights / SAMPLES; + int outputSampleIx = -1; + float weightSoFar = -Random.value * sampleWidth; + for(int i=0; i<SAMPLES; i++) + { + float sampleDist = i * sampleWidth; + while(sampleDist >= 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<tris; j++) + { + area[j] *= invTotalArea * 0.5f; + triIndices[j] = j; + } + + // Sort triangle indices by area (probability) + // Smaller -> 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<int[]>(); // 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<tris; j++) + { + area[j] *= invMinArea; + area[j] = Mathf.Round(area[j]); + } + + int skipped = 0; + var uniformTriList = new List<int>(); + for(int j=0; j<tris; j++) + { + var tarea = area[j]; + if (tarea > 0 && tarea < 65536) + { + for(int k=0; k<tarea; k++) + { + uniformTriList.Add(j); + } + } + else + { + skipped++; + } + } + + if (skipped > 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<SAMPLES; sample++) + { +#if OPTIMIZEDAREA2 + int tri = randomTriIndices[sample]; + +#elif OPTIMIZEDAREA + int tri = GetRandomTriFromBSP(bspRoot, Random.value); + //Debug.LogError(tri); +#else + int rndTri = Random.Range(0, uniformTriList.Count); + int tri = uniformTriList.Count > 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<s.Length; i++) f.Write(s[i]); + f.Write((byte)0); + f.Write((byte)'\n'); + } + + static bool SavePointLightTexture(UnityEngine.Object tex, string folder, string texName, bool isCookie, bool isCubemap, bool isIES) + { + if (File.Exists(folder + "/" + texName)) return true; + + if (isCookie) + { + // 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 as Texture, ftBuildGraphics.TexInputType.FloatColor); + ftBuildGraphics.SaveCookieFromRAM(a, + folder + "/" + texName + ); + } + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName); + } + else if (isCubemap) + { + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11) + { + ftBuildGraphics.InitShaders(); + ftBuildGraphics.SaveSky((tex as Cubemap).GetNativeTexturePtr(), + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + folder + "/" + texName, + true, + false + ); + GL.IssuePluginEvent(3); // convert cubemap + } + else + { + var a = ftBuildGraphics.InputDataFromCubemap(tex as Texture, Matrix4x4.identity, true, false, ftBuildGraphics.TexInputType.FloatColor); + ftBuildGraphics.SaveCookieFromRAM(a, + folder + "/" + texName + ); + } + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName); + } + else if (isIES) + { + var startInfo = new System.Diagnostics.ProcessStartInfo(); + startInfo.CreateNoWindow = false; + startInfo.UseShellExecute = false; + startInfo.WorkingDirectory = folder; + startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/ies2tex.exe"; + startInfo.CreateNoWindow = true; + //startInfo.Arguments = "../" + AssetDatabase.GetAssetPath(obj.iesFile) + " " + "cookie" + obj.UID + ".dds 1"; + startInfo.Arguments = "\"" + Application.dataPath + "/../" + AssetDatabase.GetAssetPath(tex) + "\" " + texName + " 1"; + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName); + +#if LAUNCH_VIA_DLL + startInfo.Arguments = "\"" + Application.dataPath + "/../" + AssetDatabase.GetAssetPath(tex) + "\" \"" + folder + "/" + texName + "\" 1"; + Debug.Log("Running ies2tex " + startInfo.Arguments); + var crt = ftRenderLightmap.ProcessCoroutine("ies2tex", startInfo.Arguments, false); + while(crt.MoveNext()) {} + int errCode = ftRenderLightmap.lastReturnValue; +#else + var exeProcess = System.Diagnostics.Process.Start(startInfo); + exeProcess.WaitForExit(); + int errCode = exeProcess.ExitCode; +#endif + if (errCode!=0) + { + ftRenderLightmap.DebugLogError("ies2tex error: "+ ftErrorCodes.TranslateI2T(errCode)); + Debug.LogError("ies2tex error: "+ ftErrorCodes.TranslateI2T(errCode) + " with args " + startInfo.Arguments); + return false; + } + } + return true; + } + + static public bool BuildLights(BakeryPointLight[] allPoints, int start, int end, bool[] skipLight, + int sampleDiv, bool ignoreNormal = false, string outName = "batchpointlight.bin") + { + if (!allowOverwrite && lightSaved.ContainsKey(outName)) return false; + lightSaved[outName] = true; + + //Debug.LogError("---Build:"); + + bool isError = false; + + var folder = ftBuildGraphics.scenePath; + if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); + var flights = new BinaryWriter(File.Open(folder + "/" + outName, FileMode.Create)); + if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(outName); + + int numLights = 0; + for(int L=start; L<=end; L++) + { + if (!skipLight[L]) numLights++; + } + flights.Write(numLights); + + for(int i=start; i<=end; i++) + { + if (skipLight[i]) continue; + + //Debug.LogError(obj.name); + + var obj = allPoints[i]; + + var tform = obj.transform; + var projMode = obj.projMode; + + if (projMode == BakeryPointLight.ftLightProjectionMode.Cookie && obj.cookie == null) + { + Debug.LogError("No cookie texture is set for light " + obj.name); + projMode = BakeryPointLight.ftLightProjectionMode.Omni; + } + if (projMode == BakeryPointLight.ftLightProjectionMode.Cubemap && obj.cubemap == null) + { + Debug.LogError("No cubemap set for light " + obj.name); + projMode = BakeryPointLight.ftLightProjectionMode.Omni; + } + if (projMode == BakeryPointLight.ftLightProjectionMode.IES && obj.iesFile == null) + { + Debug.LogError("No IES file is set for light " + obj.name); + projMode = BakeryPointLight.ftLightProjectionMode.Omni; + } + + Vector3 right, up, forward; + if (projMode == BakeryPointLight.ftLightProjectionMode.IES && obj.directionMode == BakeryPointLight.Direction.PositiveZ) + { + right = tform.right; + up = -tform.forward; + forward = tform.up; + } + else + { + right = tform.right; + up = tform.up; + forward = tform.forward; + } + + float projParam1 = 0.0f; + float projParam2 = 0.0f; + if (projMode == BakeryPointLight.ftLightProjectionMode.Cone) + { + projParam1 = obj.angle; + projParam2 = obj.innerAngle / 100.0f; + } + else if (projMode == BakeryPointLight.ftLightProjectionMode.IES) + { + projMode = BakeryPointLight.ftLightProjectionMode.Cubemap; + } + else if (projMode == BakeryPointLight.ftLightProjectionMode.Cookie) + { + projParam2 = obj.angle; + } + + flights.Write((float)projMode); + flights.Write(tform.position.x); + flights.Write(tform.position.y); + flights.Write(tform.position.z); + + flights.Write(obj.shadowSpread); + flights.Write(obj.realisticFalloff ? 1.0f : ((1.0f / obj.cutoff) * 5.0f)); + flights.Write(obj.realisticFalloff ? (obj.falloffMinRadius * obj.falloffMinRadius) : 1.0f); + flights.Write(1.0f / obj.cutoff); + + flights.Write(obj.color.linear.r * obj.intensity); + flights.Write(obj.color.linear.g * obj.intensity); + flights.Write(obj.color.linear.b * obj.intensity); + flights.Write(projParam1); + + flights.Write(right.x); + flights.Write(right.y); + flights.Write(right.z); + flights.Write(projParam2); + + flights.Write(up.x); + flights.Write(up.y); + flights.Write(up.z); + flights.Write(obj.indirectIntensity * ftRenderLightmap.hackIndirectBoost); + + flights.Write(forward.x); + flights.Write(forward.y); + flights.Write(forward.z); + + int samples = obj.samples; + if (samples > 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<Texture2D> maps) + { + for(int i=0; i<maps.Count; i++) + { + RemoveFiles(maps[i]); + } + } + + public static void ClearBakedData(SceneClearingMode sceneClearMode, bool removeLightmapFiles) + { + if (removeLightmapFiles) + { + var sceneCount = SceneManager.sceneCount; + for(int i=0; i<sceneCount; i++) + { + var scene = SceneManager.GetSceneAt(i); + if (!scene.isLoaded) continue; + var go = ftLightmaps.FindInScene("!ftraceLightmaps", scene); + if (go == null) continue; + var storage = go.GetComponent<ftLightmapsStorage>(); + 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<GameObject>(); + var sceneCount = SceneManager.sceneCount; + for(int i=0; i<sceneCount; i++) + { + var scene = SceneManager.GetSceneAt(i); + if (!scene.isLoaded) continue; + var go = ftLightmaps.FindInScene("!ftraceLightmaps", scene); + if (go == null) continue; + var storage = go.GetComponent<ftLightmapsStorage>(); + if (storage != null) + { + var newGO = new GameObject(); + var newStorage = newGO.AddComponent<ftLightmapsStorage>(); + ftLightmapsStorage.CopySettings(storage, newStorage); + newStorages.Add(newGO); + } + Undo.DestroyObjectImmediate(go); + } + LightmapSettings.lightmaps = new LightmapData[0]; + for(int i=0; i<newStorages.Count; i++) + { + newStorages[i].name = "!ftraceLightmaps"; + } + } + else if (sceneClearMode == SceneClearingMode.lightmapReferencesAndBakeSettings) + { + var sceneCount = SceneManager.sceneCount; + for(int i=0; i<sceneCount; i++) + { + var scene = SceneManager.GetSceneAt(i); + if (!scene.isLoaded) continue; + var go = ftLightmaps.FindInScene("!ftraceLightmaps", scene); + if (go == null) continue; + Undo.DestroyObjectImmediate(go); + } + LightmapSettings.lightmaps = new LightmapData[0]; + } + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClearMenu.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClearMenu.cs.meta new file mode 100644 index 00000000..afc5db72 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClearMenu.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6976907e066f1824581718bc451446bc +timeCreated: 1534329905 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClient.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClient.cs new file mode 100644 index 00000000..7c6baca1 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftClient.cs @@ -0,0 +1,505 @@ +using UnityEngine; +using UnityEditor.SceneManagement; +using System.Text; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; + +public class ftClient +{ + public const byte SERVERTASK_COPY = 0; + public const byte SERVERTASK_FTRACE = 1; + public const byte SERVERTASK_FTRACERTX = 2; + public const byte SERVERTASK_COMBINEMASKS = 3; + + public const byte SERVERTASK_DENOISE5 = 4; + public const byte SERVERTASK_DENOISE6 = 5; + public const byte SERVERTASK_DENOISE7 = 6; + public const byte SERVERTASK_DENOISEOIDN = 7; + + public const byte SERVERTASK_HF2HDR = 8; + public const byte SERVERTASK_RGBA2TGA = 9; + public const byte SERVERTASK_SEAMFIX = 10; + + public const byte SERVERTASK_LMREBAKE = 11; + public const byte SERVERTASK_LMREBAKESIMPLE = 12; + public const byte SERVERTASK_LODGEN = 13; + public const byte SERVERTASK_LODGENINIT = 14; + public const byte SERVERTASK_GIPARAMS = 15; + public const byte SERVERTASK_RECEIVEFILE = 16; + public const byte SERVERTASK_REPORTSTATUS = 17; + public const byte SERVERTASK_SETSCENENAME = 18; + public const byte SERVERTASK_GETDATA = 19; + public const byte SERVERTASK_GETDATAREADY = 20; + + public const byte SERVERERROR_IDLE = 0; + public const byte SERVERERROR_COPY = 1; + public const byte SERVERERROR_EXEC = 2; + public const byte SERVERERROR_APPERR = 3; + public const byte SERVERERROR_GIPARAMS = 4; + public const byte SERVERERROR_NOTIMPLEMENTED = 5; + public const byte SERVERERROR_UNKNOWNTASK = 6; + public const byte SERVERERROR_BUSY = 7; + public const byte SERVERERROR_UNKNOWN = 8; + public const byte SERVERERROR_SCENENAMETOOLONG = 9; + public const byte SERVERERROR_FILENOTFOUND = 10; + public const byte SERVERERROR_FILEHASZEROSIZE = 11; + public const byte SERVERERROR_NOMEM = 12; + public const byte SERVERERROR_INCORRECT = 13; + public const byte SERVERERROR_INCORRECTFILENAME = 14; + public const byte SERVERERROR_WRITEFAILED = 15; + public const byte SERVERERROR_INCORRECTARGS = 16; + public const byte SERVERERROR_FILESIZE = 17; + public const byte SERVERERROR_STATUSLIMIT = 18; + + public static string serverAddress = "127.0.0.1"; + const int serverPort = 27777; + public static bool connectedToServer = false; + public static string lastServerMsg = "Server: no data"; + public static string lastServerScene = ""; // last baked scene + public static int lastServerErrorCode = 0; + public static bool lastServerMsgIsError = false; + public static bool serverGetDataMode = false; // if we're in download mode or status polling mode + public static bool serverMustRefreshData = false; // if ready to apply downloaded data + + static string lastServerFile = ""; // last file loaded via GETDATA on the server + static int lastServerFileHash = 0; + static int lastServerFileSize = 0; + static double timeToUpdateServerStatus = 0; + static double serverStatusInterval = 1000.0; + static double serverConnectionTimeout = 2000.0; + + static Socket statusSocket; + //static System.Threading.Thread statusThread; + static IEnumerator statusProc; + + public static Dictionary<string, byte> app2serverTask = new Dictionary<string, byte> + { + {"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<string> 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<serverFileList.Count; i++) + { + var fsoc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + fsoc.Connect(remoteEP); + if (!fsoc.Poll(0, SelectMode.SelectWrite)) return false; + + var sceneFile = File.ReadAllBytes(ftRenderLightmap.scenePath + "/" + serverFileList[i]); + int headerSize = 5 + serverFileList[i].Length + 1; + var buff = new byte[sceneFile.Length + headerSize]; + + int numTasks = 1; + System.Buffer.BlockCopy(System.BitConverter.GetBytes(numTasks), 0, buff, 0, 4); + buff[4] = SERVERTASK_RECEIVEFILE; + buff[5] = (byte)serverFileList[i].Length; + for(int j=0; j<serverFileList[i].Length; j++) buff[6+j] = (byte)serverFileList[i][j]; + System.Buffer.BlockCopy(sceneFile, 0, buff, headerSize, sceneFile.Length); + + fsoc.Send(buff); + fsoc.Close(); + } + + soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + soc.Connect(remoteEP); + if (!soc.Poll(0, SelectMode.SelectWrite)) return false; + soc.Send(renderSequence); + soc.Close(); + return true; + } + + public static void ServerGetData(List<string> 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<BakeryDirectLight>(); + 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<BakerySkyLight>(); + 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<BakeryPointLight>(); + 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<BakeryLightMesh>(); + 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<MeshRenderer>().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<BakeryPointLight>(); + 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<BakeryVolume>(); + 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<Light>(); + 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<Light>(); + 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<Light>(); + 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<int, string> ftraceMap = new Dictionary<int, string> + { + {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<int, string> combineMasksMap = new Dictionary<int, string> + { + {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<int, string> denoiserMap = new Dictionary<int, string> + { + {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<int, string> h2hMap = new Dictionary<int, string> + { + {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<int, string> i2tMap = new Dictionary<int, string> + { + {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<int, string> seamfixerMap = new Dictionary<int, string> + { + {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<int, string> lmrMap = new Dictionary<int, string> + { + {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<int, string> serverMap = new Dictionary<int, string> + { + {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<int, string> serverAppMap = new Dictionary<int, string> + { + {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<BakeryLightmapGroup>(); + 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<MeshRenderer>(); + var mf = selectedLight.GetComponent<MeshFilter>(); + var areaLight = selectedLight.GetComponent<Light>(); + 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<mats.Length; i++) + { + var mat = mats[i]; + if (singleMat == null) singleMat = mat; + if (mat != null && mat != singleMat) + { + showError = true; + showErrorText = "Error: different materials in mesh"; + //match = false; + materialValid[iterator] = false; + numMaterialValid--; + break; + } + if (mat == null) + { + showError = true; + showErrorText = "Error: mesh doesn't have all materials set"; + //match = false; + materialValid[iterator] = false; + numMaterialValid--; + break; + } + bool usesftlight = mat.shader.name == ftLightShaderName; + bool usesUnlitColor = mat.shader.name == "Unlit/Color"; + bool usesUnlitTexture = mat.shader.name == "Unlit/Texture"; + if (!usesftlight && !usesUnlitColor && !usesUnlitTexture) + { + showError = true; + showErrorText = "Warning: material should output unlit color"; + //match = false; + materialValid[iterator] = false; + numMaterialValid--; + break; + } + if (intensity > 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<MeshRenderer>(); + var areaLight = selectedLight.GetComponent<Light>(); + 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<MeshRenderer>(); + var areaLight = selectedLight.GetComponent<Light>(); + 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<Light> 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<lights.Count; i++) + { + var so = new SerializedObject(lights[i]); + inspectorModeInfo.SetValue(so, InspectorMode.Debug, null); + long fileid = so.FindProperty("m_LocalIdentfierInFile").longValue; + f.Write(fileid); + f.Write(0); + f.Write(0); + } + f.Write(lights.Count); + for(int i=0; i<lights.Count; i++) + { + var so = new SerializedObject(lights[i]); + var channel = so.FindProperty("m_BakingOutput").FindPropertyRelative("occlusionMaskChannel").intValue; + + int val1 = subtractive ? 0 : -1; + int val2 = subtractive ? 131076 : 131080; + + f.Write(val1); + f.Write(channel); + f.Write(val2); + } + f.Write(bytesP3); + f.Close(); + } + catch + { + ftRenderLightmap.DebugLogError("Failed to generate LightingDataAsset"); + success = false; + throw; + } + return success; + } + +#if UNITY_2017_3_OR_NEWER +#else + // Patches existing LightingDataAsset shadowmask channels + public static bool PatchShadowmaskLightingData(string inName, string outName, ref Dictionary<long,long> inID2OutID, ref Dictionary<long,int> 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<bytesIn.Length; i++) + { + var val = bytesIn[i]; + for(int j=0; j<lightCount; j++) + { + var expectedVal = matches[j] >= 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<lightsAsWrittenCounter; i++) + { + int id = lightsAsWritten[i]; + var channelBytes = outChannelsAsBytes[id]; + int channelStartAddr = firstAddressReplaced + 16 * lightCount + 4 + 12 * i + 4; + if (subtractive) + { + for(int j=0; j<4; j++) + { + bytesIn[channelStartAddr + j - 4] = 0; + } + } + for(int j=0; j<4; j++) + { + bytesIn[channelStartAddr + j] = channelBytes[j]; + } + if (subtractive) + { + var val2 = BitConverter.GetBytes(131076); + for(int j=0; j<4; j++) + { + bytesIn[channelStartAddr + j + 4] = val2[j]; + } + } + } + + var f = new BinaryWriter(File.Open(outName, FileMode.Create)); + f.Write(bytesIn); + f.Close(); + ftRenderLightmap.DebugLogInfo("PatchShadowmaskLightingData: patched " + replaced + " lights"); + } + catch + { + ftRenderLightmap.DebugLogError("Failed to patch LightingDataAsset"); + return false; + } + + return true; + } +#endif +} + +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightingDataGen.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightingDataGen.cs.meta new file mode 100644 index 00000000..89bdc063 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightingDataGen.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 04a9623f45d46d64b8d852e0b0ea9a81 +timeCreated: 1535032797 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmappedPrefabInspector.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmappedPrefabInspector.cs new file mode 100644 index 00000000..1a31640e --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftLightmappedPrefabInspector.cs @@ -0,0 +1,117 @@ +using UnityEditor; +using UnityEngine; + +[CustomEditor(typeof(BakeryLightmappedPrefab))] +[CanEditMultipleObjects] +public class ftLightmappedPrefabInspector : UnityEditor.Editor +{ + bool allPrefabsGood = true; + SerializedProperty isEnabled; + + void Refresh(BakeryLightmappedPrefab selected) + { + allPrefabsGood = selected.IsValid() && allPrefabsGood; + } + + void OnPrefabInstanceUpdate(GameObject go) + { + allPrefabsGood = true; + foreach(BakeryLightmappedPrefab selected in targets) + { + //if (go != selected.gameObject) continue; + Refresh(selected); + } + } + + void OnEnable() + { + allPrefabsGood = true; + foreach(BakeryLightmappedPrefab selected in targets) + { + Refresh(selected); + } + PrefabUtility.prefabInstanceUpdated += OnPrefabInstanceUpdate; + isEnabled = serializedObject.FindProperty("enableBaking"); + } + + void OnDisable() + { + PrefabUtility.prefabInstanceUpdated -= OnPrefabInstanceUpdate; + } + + public ftLightmapsStorage FindPrefabStorage(BakeryLightmappedPrefab pref) + { + var p = pref.gameObject; + var bdataName = "BakeryPrefabLightmapData"; + var pstoreT = p.transform.Find(bdataName); + if (pstoreT == null) + { + var pstoreG = new GameObject(); + pstoreG.name = bdataName; + pstoreT = pstoreG.transform; + pstoreT.parent = p.transform; + } + var pstore = pstoreT.gameObject.GetComponent<ftLightmapsStorage>(); + if (pstore == null) pstore = pstoreT.gameObject.AddComponent<ftLightmapsStorage>(); + 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<string, bool> assetHasPaddingAdjustment = new Dictionary<string, bool>(); + static Dictionary<string, ftSavedPadding2> assetSavedPaddingAdjustment = new Dictionary<string, ftSavedPadding2>(); + +#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<props.Length; p++) + { + if (props[p].Substring(0,7) == "#BAKERY") + { + hasGlobalPaddingAdjustment2 = true; + break; + } + } +#endif + var savedAdjustment = AssetDatabase.LoadAssetAtPath( + Path.GetDirectoryName(assetPath) + "/" + Path.GetFileNameWithoutExtension(assetPath) + "_padding.asset", typeof(ftSavedPadding2)) as ftSavedPadding2; + + if (!hasGlobalPaddingAdjustment && !hasGlobalPaddingAdjustment2 && savedAdjustment == null) return; + + assetHasPaddingAdjustment[assetPath] = importer.generateSecondaryUV; + importer.generateSecondaryUV = false; // disable built-in unwrapping for models with padding adjustment + assetSavedPaddingAdjustment[assetPath] = savedAdjustment; + } + + void OnPostprocessModel(GameObject g) + { + ModelImporter importer = (ModelImporter)assetImporter; + if (importer.generateSecondaryUV || assetHasPaddingAdjustment[assetPath]) + { + if (!importer.generateSecondaryUV) + { + importer.generateSecondaryUV = true; // set "generate lightmap UVs" checkbox back + EditorUtility.SetDirty(importer); + } + + // Auto UVs: Adjust UV padding per mesh + //if (!storage.modifiedAssetPathList.Contains(assetPath) && g.tag == "BakeryProcessed") return; + //if (ftLightmaps.IsModelProcessed(assetPath)) return; + + //g.tag = "BakeryProcessed"; + var saved = assetSavedPaddingAdjustment[assetPath]; + if (saved != null) + { + Debug.Log("Bakery: processing auto-unwrapped asset (saved UV padding) " + assetPath); + } + else + { + Debug.Log("Bakery: processing auto-unwrapped asset " + assetPath); + } + if (storage != null) ftLightmaps.MarkModelProcessed(assetPath, true); + + uparams = new UnwrapParam(); + UnwrapParam.SetDefaults(out uparams); + uparams.angleError = importer.secondaryUVAngleDistortion * 0.01f; + uparams.areaError = importer.secondaryUVAreaDistortion * 0.01f; + uparams.hardAngle = importer.secondaryUVHardAngle; + +#if UNITY_2017_1_OR_NEWER + deserializedSuccess = false; + var props = importer.extraUserProperties; + for(int p=0; p<props.Length; p++) + { + if (props[p].Substring(0,7) == "#BAKERY") + { + var json = props[p].Substring(7); + deserialized = JsonUtility.FromJson<ftGlobalStorage.AdjustedMesh>(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<bytes.Length; i++) + { + if (bytes[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<MeshFilter>(); + 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<MeshFilter>(); + 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<Light>(); + 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<Light>(); + 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<Light>(); + 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<System.Diagnostics.ProcessStartInfo> deferredCommands; + static Dictionary<int, List<string>> deferredCommandsFallback; + static Dictionary<int, BakeryLightmapGroupPlain> deferredCommandsRebake; + static Dictionary<int, int> deferredCommandsLODGen; + static Dictionary<int, Vector3> deferredCommandsGIGen; + static Dictionary<int, BakeryLightmapGroupPlain> deferredCommandsHalf2VB; + static Dictionary<int, bool> deferredCommandsUVGB; + static List<string> deferredFileSrc; + static List<string> deferredFileDest; + static List<string> 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<ProbeEventArgs> 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<List<List<string>>> lightmapMasks; + static List<List<List<string>>> lightmapMaskLMNames; + static List<List<List<Light>>> lightmapMaskLights; + static List<List<List<bool>>> lightmapMaskDenoise; +#if UNITY_2017_3_OR_NEWER +#else + static List<Light> maskedLights; + PropertyInfo inspectorModeInfo; +#endif + static List<bool> lightmapHasColor; + static List<bool> lightmapHasMask; + static List<bool> lightmapHasDir; + static List<bool> 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<Scene, ftLightmapsStorage> 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<ReflectionProbe> 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<string, bool> lmnameComposed; + + static GUIStyle foldoutStyle; + + static BakeryVolume[] lastFoundBakeableVolumes = null; + + List<BakeryLightmapGroupPlain> groupListPlain; + List<BakeryLightmapGroupPlain> 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<int, int> shadowmaskGroupIDToChannel; + + static List<GameObject> 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<string> loadedScenes; + static List<bool> loadedScenesEnabled; + static List<bool> loadedScenesActive; + static Scene loadedDummyScene; + static bool scenesUnloaded = false; + static public void UnloadScenes() + { + EditorSceneManager.MarkAllScenesDirty(); + EditorSceneManager.SaveOpenScenes(); + + loadedScenes = new List<string>(); + loadedScenesEnabled = new List<bool>(); + loadedScenesActive = new List<bool>(); + var sceneCount = EditorSceneManager.sceneCount; + var activeScene = EditorSceneManager.GetActiveScene(); + for(int i=0; i<sceneCount; i++) + { + var s = EditorSceneManager.GetSceneAt(i); + loadedScenes.Add(s.path); + loadedScenesEnabled.Add(s.isLoaded); + loadedScenesActive.Add(s == activeScene); + } + + loadedDummyScene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); + SceneManager.SetActiveScene(loadedDummyScene); + RenderSettings.skybox = null; + scenesUnloaded = true; + } + + public bool TestSystemSpecs() + { + if (SystemInfo.graphicsShaderLevel < 30) + { + DebugLogError("Bakery requires at least Shader Model 3.0 to work. Make sure you are not currently using graphics emulation of old shader models."); + return false; + } + + /* + if (SystemInfo.graphicsDeviceType != GraphicsDeviceType.Direct3D11) + { + DebugLogError("Bakery requires Unity editor to be running in DX11 mode during the bake."); + return false; + } + */ + + if (!Directory.Exists(scenePath)) + { + var defaultPath = System.Environment.GetEnvironmentVariable("TEMP", System.EnvironmentVariableTarget.Process) + "\\frender"; + + ProgressBarEnd(); + bool cont = true; + if (verbose) + { + cont = EditorUtility.DisplayDialog("Bakery", "Chosen temp path cannot be found:\n\n" + scenePath + "\n\nYou can cancel and set a different path in Advanced Settings or just use the default one:\n\n" + defaultPath + "\n", "Use default", "Cancel"); + } + else + { + Debug.LogError("Chosen temp path was not found and set to default"); + } + if (cont) + { + scenePath = defaultPath; + ftBuildGraphics.scenePath = scenePath; + scenePathQuoted = "\"" + scenePath + "\""; + } + else + { + return false; + } + } + + if (gstorage != null) + { + // last optimal settings detection ran on another GPU + if (gstorage.gpuName != SystemInfo.graphicsDeviceName) + { + foundCompatibleSetup = false; // ask again + } + else + { + if (!rtxMode && !gstorage.runsNonRTX) + { + if (gstorage.alwaysEnableRTX) + { + DebugLogInfo("Automatically enabled RTX"); + rtxMode = true; + ftraceExe = ftraceExe6; + ftBuildGraphics.exportTerrainAsHeightmap = false; + SaveRenderSettings(); + } + else + { + int choice = EditorUtility.DisplayDialogComplex("Bakery", "This scene has RTX disabled. It is recommended to enable it on your GPU.", "Enable", "Always enable", "Don't enable"); + if (choice < 2) + { + // Enable or Always Enable + rtxMode = true; + ftraceExe = ftraceExe6; + ftBuildGraphics.exportTerrainAsHeightmap = false; + SaveRenderSettings(); + + if (choice == 1) + { + // Always Enable + gstorage.alwaysEnableRTX = true; + EditorUtility.SetDirty(gstorage); + } + } + } + } + + if (denoise) + { + if (denoiserType == ftGlobalStorage.DenoiserType.Optix5 && !gstorage.runsOptix5) + { + int choice = EditorUtility.DisplayDialogComplex("Bakery", "This scene has denoiser set to OptiX 5, 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.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<sceneCount; i++) + { + EditorSceneManager.OpenScene(loadedScenes[i], loadedScenesEnabled[i] ? OpenSceneMode.Additive : OpenSceneMode.AdditiveWithoutLoading); + } + if (loadedDummyScene.isLoaded) EditorSceneManager.UnloadSceneAsync(loadedDummyScene); + scenesUnloaded = false; + } + + bool ServerGetData() + { + var storageGO = ftLightmaps.FindInScene("!ftraceLightmaps", EditorSceneManager.GetActiveScene()); + if (storageGO == null) return false; + var storage = storageGO.GetComponent<ftLightmapsStorage>(); + 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<numLMs; i++) + { + lodLevels[i] = (int)flodInfo.ReadByte(); + if (lodLevels[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<numTraceVerts; k++) + { + fvbtraceTex2.Write(vbtraceTexPosNormalArray[k * 6]); + fvbtraceTex2.Write(vbtraceTexPosNormalArray[k * 6 + 1]); + fvbtraceTex2.Write(vbtraceTexPosNormalArray[k * 6 + 2]); + fvbtraceTex2.Write(vbtraceTexPosNormalArray[k * 6 + 3]); + fvbtraceTex2.Write(vbtraceTexPosNormalArray[k * 6 + 4]); + fvbtraceTex2.Write(vbtraceTexPosNormalArray[k * 6 + 5]); + + int id2 = (int)(vbtraceTexUVArray[k * 2]/10); + //if ((int)(vbtraceTexUVArray[k * 2]/10) == i) + if (id2 < 0 || lodLevels[id2] == 0) + { + // own lightmap is full resoltion + fvbtraceTex2.Write(vbtraceTexUVArray[k * 2]); + fvbtraceTex2.Write(vbtraceTexUVArray[k * 2 + 1]); + } + else + { + // other lightmaps use LODs + fvbtraceTex2.Write(vbtraceTexUVArrayLOD[k * 2]); + fvbtraceTex2.Write(vbtraceTexUVArrayLOD[k * 2 + 1]); + } + } + fvbtraceTex2.Close(); + return 0; + } + + int SampleCount(int samples) + { + if (samples == 0) return 0; + return System.Math.Max(samples / sampleDivisor,1); + } + + bool GroupAffectedByGroup(int curSceneLodLevel, int otherSceneLodLevel) + { + if (curSceneLodLevel < 0) + { + if (otherSceneLodLevel > 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<int> 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<str.Length; i++) + { + flist.Write(str[i]); + } + byte zeroByte = 0; + flist.Write(zeroByte); + } + + Convex GetSpotConvex(Light lb) + { + var lbT = lb.transform; + + // radius computed same way as in BakeryPointLight gizmo drawing code + float angle = lb.spotAngle; + float angle2 = (180 - angle) * Mathf.Deg2Rad * 0.5f; + float x = 1 / Mathf.Sin(angle2); + x = Mathf.Sqrt(x * x - 1); + float radius = x * lb.range; + + var bfar = lbT.position + lbT.forward * lb.range; + var bright = lbT.right * radius; + var bup = lbT.up * radius; + + var bvertices = new Vector3[5]; + bvertices[0] = lbT.position; + bvertices[1] = (bfar - bright) - bup; + bvertices[2] = (bfar + bright) - bup; + bvertices[3] = (bfar + bright) + bup; + bvertices[4] = (bfar - bright) + bup; + + var bplanes = new Plane[5]; + bplanes[0] = new Plane(bvertices[1], bvertices[4], bvertices[3]); // cap + bplanes[1] = new Plane(bvertices[0], bvertices[1], bvertices[2]); + bplanes[2] = new Plane(bvertices[0], bvertices[2], bvertices[3]); + bplanes[3] = new Plane(bvertices[0], bvertices[3], bvertices[4]); + bplanes[4] = new Plane(bvertices[0], bvertices[4], bvertices[1]); + + var bconvex = new Convex(); + bconvex.vertices = bvertices; + bconvex.planes = bplanes; + + return bconvex; + } + + bool ConvexIntersect(Convex a, Convex b) + { + // all B verts must be on the same side of at least one plane of A + for(int p=0; p<a.planes.Length; p++) + { + bool outside = true; + for(int v=0; v<b.vertices.Length; v++) + { + outside = !a.planes[p].GetSide(b.vertices[v]); + if (!outside) break; // intersects or inside + } + if (outside) return false; + } + return true; + } + + bool ConvexSphereIntersect(Convex a, Vector3 bpos, float bradius) + { + // sphere must be outside of any plane with distance >= radius + for(int p=0; p<a.planes.Length; p++) + { + float d = -a.planes[p].GetDistanceToPoint(bpos); + if (d > 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>(); + int totalVertexCount = 0; + for(int i=0; i<storage.bakedIDs.Count; i++) + { + if (storage.bakedIDs[i] != LMID) continue; + var mr = storage.bakedRenderers[i]; + //var vertexOffset = storage.bakedVertexOffset[i]; + + //vertexOffsetLengths.Add(vertexOffset); + var sharedMesh = ftBuildGraphics.GetSharedMesh(mr); + int vertexCount = sharedMesh.vertexCount; + //vertexOffsetLengths.Add(vertexCount); + + totalVertexCount += vertexCount; + } + + if (totalVertexCount == 0) return 0; + + int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)totalVertexCount)); + atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)tileSize) * tileSize; + + var vertColors = new byte[atlasTexSize * atlasTexSize * 4]; + byte[] vertColorsMask = null; + byte[] vertColorsDir = null; + byte[] vertColorsSHL1x = null; + byte[] vertColorsSHL1y = null; + byte[] vertColorsSHL1z = null; + if (hasShadowMask) vertColorsMask = new byte[atlasTexSize * atlasTexSize * 4]; + if (hasDir) vertColorsDir = new byte[atlasTexSize * atlasTexSize * 4]; + if (hasSH) + { + vertColorsSHL1x = new byte[atlasTexSize * atlasTexSize * 4]; + vertColorsSHL1y = new byte[atlasTexSize * atlasTexSize * 4]; + vertColorsSHL1z = new byte[atlasTexSize * atlasTexSize * 4]; + } + + var sceneCount = SceneManager.sceneCount; + + GCHandle handle = GCHandle.Alloc(vertColors, GCHandleType.Pinned); + GCHandle handleMask = new GCHandle(); + GCHandle handleDir = new GCHandle(); + GCHandle handleL1x = new GCHandle(); + GCHandle handleL1y = new GCHandle(); + GCHandle handleL1z = new GCHandle(); + if (hasShadowMask) handleMask = GCHandle.Alloc(vertColorsMask, GCHandleType.Pinned); + if (hasDir) handleDir = GCHandle.Alloc(vertColorsDir, GCHandleType.Pinned); + if (hasSH) + { + handleL1x = GCHandle.Alloc(vertColorsSHL1x, GCHandleType.Pinned); + handleL1y = GCHandle.Alloc(vertColorsSHL1y, GCHandleType.Pinned); + handleL1z = GCHandle.Alloc(vertColorsSHL1z, GCHandleType.Pinned); + } + try + { + System.IntPtr pointer = handle.AddrOfPinnedObject(); + System.IntPtr pointerMask = (System.IntPtr)0; + System.IntPtr pointerDir = (System.IntPtr)0; + System.IntPtr pointerL1x = (System.IntPtr)0; + System.IntPtr pointerL1y = (System.IntPtr)0; + System.IntPtr pointerL1z = (System.IntPtr)0; + if (hasShadowMask) pointerMask = handleMask.AddrOfPinnedObject(); + if (hasDir) pointerDir = handleDir.AddrOfPinnedObject(); + if (hasSH) + { + pointerL1x = handleL1x.AddrOfPinnedObject(); + pointerL1y = handleL1y.AddrOfPinnedObject(); + pointerL1z = handleL1z.AddrOfPinnedObject(); + } + + errCode = halffloat2vb(scenePath + "\\" + lmname + (hasSH ? "_final_L0" : "_final_HDR") + (compressedOutput ? ".lz4" : ".dds"), pointer, 0); + + if (hasShadowMask) + errCode2 = halffloat2vb(scenePath + "\\" + lmname + "_Mask" + (compressedOutput ? ".lz4" : ".dds"), pointerMask, 1); + if (hasDir) + errCode3 = halffloat2vb(scenePath + "\\" + lmname + "_final_Dir" + (compressedOutput ? ".lz4" : ".dds"), pointerDir, 1); + + if (hasSH) + { + errCode4 = halffloat2vb(scenePath + "\\" + lmname + "_final_L1x" + (compressedOutput ? ".lz4" : ".dds"), pointerL1x, 1); + errCode5 = halffloat2vb(scenePath + "\\" + lmname + "_final_L1y" + (compressedOutput ? ".lz4" : ".dds"), pointerL1y, 1); + errCode6 = halffloat2vb(scenePath + "\\" + lmname + "_final_L1z" + (compressedOutput ? ".lz4" : ".dds"), pointerL1z, 1); + } + + if (errCode == 0 && errCode2 == 0 && errCode3 == 0 && errCode4 == 0 && errCode5 == 0 && errCode6 == 0) + { + for(int i=0; i<storage.bakedIDs.Count; i++) + { + if (storage.bakedIDs[i] != LMID) continue; + var mr = storage.bakedRenderers[i]; + var vertexOffset = storage.bakedVertexOffset[i]; + + var mesh = ftBuildGraphics.GetSharedMesh(mr); + int vertexCount = mesh.vertexCount; + + var colorBuff = new Color32[vertexCount]; + for(int j=0; j<vertexCount; j++) + { + colorBuff[j] = new Color32(vertColors[(vertexOffset + j) * 4], + vertColors[(vertexOffset + j) * 4 + 1], + vertColors[(vertexOffset + j) * 4 + 2], + vertColors[(vertexOffset + j) * 4 + 3]); + } + + var newMesh = new Mesh(); + newMesh.vertices = mesh.vertices; + newMesh.colors32 = colorBuff; + + //float packScale = 254.0f / 255.0f; + //maskBuff[j] = new Vector2(r+(g/255.0f)*packScale, b+(a/255.0f)*packScale); + + /* + uv2 + x: shadowmask + y: dir/L1x + uv3 + x: L1y + y: L1z + */ + + if (hasShadowMask || hasDir || hasSH) + { + var buff = new Vector2[vertexCount]; + byte sr = 0, sg = 0, sb = 0, sa = 0; + byte dr = 0, dg = 0, db = 0;//, da = 0; + for(int j=0; j<vertexCount; j++) + { + if (hasShadowMask) + { + sr = vertColorsMask[(vertexOffset + j) * 4]; + sg = vertColorsMask[(vertexOffset + j) * 4 + 1]; + sb = vertColorsMask[(vertexOffset + j) * 4 + 2]; + sa = vertColorsMask[(vertexOffset + j) * 4 + 3]; + } + if (hasDir) + { + dr = vertColorsDir[(vertexOffset + j) * 4]; + dg = vertColorsDir[(vertexOffset + j) * 4 + 1]; + db = vertColorsDir[(vertexOffset + j) * 4 + 2]; + //da = vertColorsDir[(vertexOffset + j) * 4 + 3]; + } + else if (hasSH) + { + dr = vertColorsSHL1x[(vertexOffset + j) * 4]; + dg = vertColorsSHL1x[(vertexOffset + j) * 4 + 1]; + db = vertColorsSHL1x[(vertexOffset + j) * 4 + 2]; + } + buff[j] = new Vector2(Pack4BytesToFloat(sr,sg,sb,sa), Pack3BytesToFloat(dr,dg,db)); + } + newMesh.uv2 = buff; + } + + if (hasSH) + { + var buff = new Vector2[vertexCount]; + byte r1,g1,b1; + byte r2,g2,b2; + for(int j=0; j<vertexCount; j++) + { + r1 = vertColorsSHL1y[(vertexOffset + j) * 4]; + g1 = vertColorsSHL1y[(vertexOffset + j) * 4 + 1]; + b1 = vertColorsSHL1y[(vertexOffset + j) * 4 + 2]; + + r2 = vertColorsSHL1z[(vertexOffset + j) * 4]; + g2 = vertColorsSHL1z[(vertexOffset + j) * 4 + 1]; + b2 = vertColorsSHL1z[(vertexOffset + j) * 4 + 2]; + + buff[j] = new Vector2(Pack3BytesToFloat(r1,g1,b1), Pack3BytesToFloat(r2,g2,b2)); + } + newMesh.uv3 = buff; + } + + //newMesh.triangles = mesh.triangles; // debug only! + + for(int s=0; s<sceneCount; s++) + { + var scene = EditorSceneManager.GetSceneAt(s); + if (!scene.isLoaded) continue; + var st = storages[scene]; + st.bakedVertexColorMesh[i] = newMesh; + } + + var outPath = "Assets/" + outputPathFull + "/" + lmname + i + ".asset"; + if (File.Exists(outPath)) ValidateFileAttribs(outPath); + AssetDatabase.CreateAsset(newMesh, outPath); + } + AssetDatabase.SaveAssets(); + } + else + { + Debug.LogError("hf2vb: " + errCode + " " + errCode2 + " " + errCode3 + " " + errCode4 + " " + errCode5 + " " + errCode6); + } + } + finally + { + if (handle.IsAllocated) handle.Free(); + if (handleMask.IsAllocated) handleMask.Free(); + if (handleDir.IsAllocated) handleDir.Free(); + if (handleL1x.IsAllocated) handleL1x.Free(); + if (handleL1y.IsAllocated) handleL1y.Free(); + if (handleL1z.IsAllocated) handleL1z.Free(); + } + + return errCode; + } + + public void RenderReflectionProbesButton(bool showMsgWindows = true) + { + restoreFromGlobalSector = false; + userCanceled = false; + if (Application.isPlaying) return; + progressFunc = RenderReflProbesFunc(); + EditorApplication.update += RenderReflProbesUpdate; + verbose = showMsgWindows; + bakeInProgress = true; + showProgressBar = true; + } + + public void RenderLightProbesButton(bool showMsgWindows = true) + { + restoreFromGlobalSector = true; + userCanceled = false; + fullSectorRender = false; + if (Application.isPlaying) return; + if (!TestSystemSpecs()) return; + if (lightProbeMode == LightProbeMode.Legacy) + { + progressFunc = RenderLightProbesFunc(); + EditorApplication.update += RenderLightProbesUpdate; + verbose = showMsgWindows; + bakeInProgress = true; + } + else if (lightProbeMode == LightProbeMode.L1) + { + selectedOnly = false; + probesOnlyL1 = true; + progressFunc = RenderLightmapFunc(); + EditorApplication.update += RenderLightmapUpdate; + verbose = showMsgWindows; + bakeInProgress = true; + } + showProgressBar = true; + } + + public void RenderButton(bool showMsgWindows = true) + { + restoreFromGlobalSector = true; + userCanceled = false; + if (Application.isPlaying) return; + if (!TestSystemSpecs()) return; + verbose = showMsgWindows; + + if (clientMode) + { + if (!ftClient.connectedToServer) + { + DebugLogError("Network rendering is enabled, but server is disconnected."); + return; + } + if (ftClient.lastServerErrorCode == ftClient.SERVERERROR_BUSY) + { + DebugLogError("Server is busy."); + return; + } + } + else + { + if (!foundCompatibleSetup && verbose) + { + if (gstorage != null) gstorage.foundCompatibleSetup = true; + foundCompatibleSetup = true; + int answer = EditorUtility.DisplayDialogComplex("Bakery", "Would you like to automatically detect optimal settings for your hardware? You can also do it later via Bakery/Utilities/Detect optimal settings", "Yes", "No", "Never"); + if (answer == 0) + { + ftDetectSettings.DetectCompatSettings(); + return; + } + else if (answer == 2) + { + gstorage.foundCompatibleSetup = foundCompatibleSetup = true; + gstorage.gpuName = SystemInfo.graphicsDeviceName; + EditorUtility.SetDirty(gstorage); + } + } + } + + if (pstorage == null) pstorage = ftLightmaps.GetProjectSettings(); + + if (pstorage.deletePreviousLightmapsBeforeBake) + { + ftClearMenu.ClearBakedData(ftClearMenu.SceneClearingMode.nothing, true); + } + + if (OnPreFullRender != null) + { + OnPreFullRender.Invoke(this, null); + } + +#if UNITY_2018_3_OR_NEWER + unityEditorHWND = GetForegroundWindow(); + var wnd = GetParent(unityEditorHWND); + while(wnd != (System.IntPtr)0) + { + unityEditorHWND = wnd; + wnd = GetParent(unityEditorHWND); + } + + var titleBuff = new StringBuilder(256); + if (GetWindowText(unityEditorHWND, titleBuff, 256) > 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("<color=#5073c9ff><b>Bakery - GPU Lightmapper by Mr F</b></color>", "Version 1.9. Go to Bakery Wiki"), linkStyle); + GUI.Label(aboutRect, new GUIContent("<color=#5073c9ff><b>____________________________</b></color>", "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<BakeryLightmapGroup>(); + var groupListGIContributing = new List<BakeryLightmapGroup>(); + 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<ReflectionProbe>(); + 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<BakeryVolume>(); + var vols2 = new List<BakeryVolume>(); + Transform sectorTform = null; + if (curSector != null) sectorTform = curSector.transform; + for(int v=0; v<vols.Length; v++) + { + if (vols[v].enableBaking) + { + if (fullSectorRender) + { + var parent = vols[v].transform.parent; + while(parent != null) + { + if (parent == sectorTform) vols2.Add(vols[v]); // only use volumes parented to current sector + parent = parent.parent; + } + } + else + { + vols2.Add(vols[v]); + } + } + } + lastFoundBakeableVolumes = vols2.ToArray(); + return lastFoundBakeableVolumes; + } + + public static int VolumeDimension(int x) + { + const float blockSize = 4.0f; + if (ftRenderLightmap.compressVolumes) return (int)(Mathf.Ceil(x/blockSize)*blockSize); + return x; + } + + void LoadVolumes() + { + var vols = FindBakeableVolumes(); + if (vols.Length == 0) return; + + int numTotalProbes = 0; + for(int v=0; v<vols.Length; v++) + { + numTotalProbes += VolumeDimension(vols[v].resolutionX) * VolumeDimension(vols[v].resolutionY) * VolumeDimension(vols[v].resolutionZ); + } + + int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)numTotalProbes)); + atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)tileSize) * tileSize; + + BakeryLightmapGroup.RenderMode pVolumeMode = (BakeryLightmapGroup.RenderMode)pstorage.volumeRenderMode; + bool shadowmask = (pVolumeMode == BakeryLightmapGroup.RenderMode.Auto && userRenderMode == RenderMode.Shadowmask) + || pVolumeMode == BakeryLightmapGroup.RenderMode.Shadowmask; + + 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]; + byte[] lshadows = null; + if (shadowmask) lshadows = new byte[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); + GCHandle handleShadows = new GCHandle(); + if (shadowmask) handleShadows = GCHandle.Alloc(lshadows, GCHandleType.Pinned); + var errCodes = new int[5]; + try + { + var pointer = handle.AddrOfPinnedObject(); + var pointerL1x = handleL1x.AddrOfPinnedObject(); + var pointerL1y = handleL1y.AddrOfPinnedObject(); + var pointerL1z = handleL1z.AddrOfPinnedObject(); + System.IntPtr pointerShadows = (System.IntPtr)0; + if (shadowmask) pointerShadows = handleShadows.AddrOfPinnedObject(); + errCodes[0] = halffloat2vb(scenePath + "\\volumes_final_L0" + (compressedOutput ? ".lz4" : ".dds"), pointer, 2); + errCodes[1] = halffloat2vb(scenePath + "\\volumes_final_L1x" + (compressedOutput ? ".lz4" : ".dds"), pointerL1x, 2); + errCodes[2] = halffloat2vb(scenePath + "\\volumes_final_L1y" + (compressedOutput ? ".lz4" : ".dds"), pointerL1y, 2); + errCodes[3] = halffloat2vb(scenePath + "\\volumes_final_L1z" + (compressedOutput ? ".lz4" : ".dds"), pointerL1z, 2); + if (shadowmask) + { + errCodes[4] = halffloat2vb(scenePath + "\\volumes_mask" + (compressedOutput ? ".lz4" : ".dds"), pointerShadows, 1); + } + bool ok = true; + for(int i=0; i<5; i++) + { + if (errCodes[i] != 0) + { + Debug.LogError("hf2vb (" + i + "): " + errCodes[i]); + ok = false; + } + } + if (ok) + { + int i = 0; + int maskI = 0; + bool actuallyCompressVolumes = false; +#if UNITY_2020_1_OR_NEWER + actuallyCompressVolumes = compressVolumes; +#endif + for(int v=0; v<vols.Length; v++) + { + var vol = vols[v]; + int rx = VolumeDimension(vol.resolutionX); + int ry = VolumeDimension(vol.resolutionY); + int rz = VolumeDimension(vol.resolutionZ); + int numProbes = rx*ry*rz; + int numProbesInSlice = rx*ry; + int lastProbeInSlice = numProbesInSlice - 1; + int compressedSliceSizeHDR = 0; + int compressedSliceSizeLDR = 0; + Color[] texData0 = null; + Color[] texData1 = null; + Color[] texData2 = null; + Color[] texData3 = null; + Texture2D texSliceHDR = null; + Texture2D texSliceLDR = null; + byte[] compressedTexData0 = null; + byte[] compressedTexData1 = null; + byte[] compressedTexData2 = null; + byte[] compressedTexData3 = null; + if (actuallyCompressVolumes) + { + // Per slice arrays + texData0 = new Color[numProbesInSlice]; + texData1 = new Color[numProbesInSlice]; + texData2 = new Color[numProbesInSlice]; + texData3 = new Color[numProbesInSlice]; + } + else + { + // Full 3D arrays + texData0 = new Color[numProbes]; + texData1 = new Color[numProbes]; + texData2 = new Color[numProbes]; + } + #if COMPRESS_VOLUME_RGBM + TextureFormat compressedHDRFormat = TextureFormat.BC7; + TextureFormat uncompressedHDRFormat = TextureFormat.ARGB32; + #else + TextureFormat compressedHDRFormat = TextureFormat.BC6H; + TextureFormat uncompressedHDRFormat = TextureFormat.RGBAHalf; + #endif + for(int z=0; z<rz; z++) + { + for(int y=0; y<ry; y++) + { + for(int x=0; x<rx; x++) + { + float l0r = l0[i*4+0] * 2; + float l0g = l0[i*4+1] * 2; + float l0b = l0[i*4+2] * 2; + + const float convL0 = ftAdditionalConfig.convL0; + const float convL1 = ftAdditionalConfig.convL1; + + float l1xr; + float l1xg; + float l1xb; + float l1yr; + float l1yg; + float l1yb; + float l1zr; + float l1zg; + float l1zb; + // read as BGR (2,1,0) + if (vol.encoding == BakeryVolume.Encoding.RGBA8 || vol.encoding == BakeryVolume.Encoding.RGBA8Mono) + { + l1xr = ((l1x[i*4+2] * 2.0f - 1.0f) * convL1) * 0.5f + 0.5f; + l1xg = ((l1x[i*4+1] * 2.0f - 1.0f) * convL1) * 0.5f + 0.5f; + l1xb = ((l1x[i*4+0] * 2.0f - 1.0f) * convL1) * 0.5f + 0.5f; + + l1yr = ((l1y[i*4+2] * 2.0f - 1.0f) * convL1) * 0.5f + 0.5f; + l1yg = ((l1y[i*4+1] * 2.0f - 1.0f) * convL1) * 0.5f + 0.5f; + l1yb = ((l1y[i*4+0] * 2.0f - 1.0f) * convL1) * 0.5f + 0.5f; + + l1zr = ((l1z[i*4+2] * 2.0f - 1.0f) * convL1) * 0.5f + 0.5f; + l1zg = ((l1z[i*4+1] * 2.0f - 1.0f) * convL1) * 0.5f + 0.5f; + l1zb = ((l1z[i*4+0] * 2.0f - 1.0f) * convL1) * 0.5f + 0.5f; + + if (vol.encoding == BakeryVolume.Encoding.RGBA8Mono) + { + // Single direction packed to tex1 + l1xr = (l1xr + l1xg + l1xb) * 0.3333333333f; + l1xg = (l1yr + l1yg + l1yb) * 0.3333333333f; + l1xb = (l1zr + l1zg + l1zb) * 0.3333333333f; + + l1yr = l1yg = l1yb = 0; + l1zr = l1zg = l1zb = 0; + } + } + else + { + l1xr = (l1x[i*4+2] * 2.0f - 1.0f) * l0r*2 * convL1; + l1xg = (l1x[i*4+1] * 2.0f - 1.0f) * l0g*2 * convL1; + l1xb = (l1x[i*4+0] * 2.0f - 1.0f) * l0b*2 * convL1; + + l1yr = (l1y[i*4+2] * 2.0f - 1.0f) * l0r*2 * convL1; + l1yg = (l1y[i*4+1] * 2.0f - 1.0f) * l0g*2 * convL1; + l1yb = (l1y[i*4+0] * 2.0f - 1.0f) * l0b*2 * convL1; + + l1zr = (l1z[i*4+2] * 2.0f - 1.0f) * l0r*2 * convL1; + l1zg = (l1z[i*4+1] * 2.0f - 1.0f) * l0g*2 * convL1; + l1zb = (l1z[i*4+0] * 2.0f - 1.0f) * l0b*2 * convL1; + + if (actuallyCompressVolumes) + { + float il0r = 1.0f / l0r; + float il0g = 1.0f / l0g; + float il0b = 1.0f / l0b; + + l1xr = (l1xr * il0r) * 0.5f + 0.5f; + l1xg = (l1xg * il0g) * 0.5f + 0.5f; + l1xb = (l1xb * il0b) * 0.5f + 0.5f; + + l1yr = (l1yr * il0r) * 0.5f + 0.5f; + l1yg = (l1yg * il0g) * 0.5f + 0.5f; + l1yb = (l1yb * il0b) * 0.5f + 0.5f; + + l1zr = (l1zr * il0r) * 0.5f + 0.5f; + l1zg = (l1zg * il0g) * 0.5f + 0.5f; + l1zb = (l1zb * il0b) * 0.5f + 0.5f; + } + } + l0r *= convL0; + l0g *= convL0; + l0b *= convL0; + + + if (actuallyCompressVolumes) + { + int index = y*rx + x; + #if COMPRESS_VOLUME_RGBM + const float rgbmMul = 1.0f / 8.0f; + l0r = Mathf.Sqrt(l0r) * rgbmMul; + l0g = Mathf.Sqrt(l0g) * rgbmMul; + l0b = Mathf.Sqrt(l0b) * rgbmMul; + float a = Mathf.Max(Mathf.Max(Mathf.Max(l0r, l0g), l0b), 1.0f / 255); + if (a > 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<compressedSliceSizeHDR; c++) compressedTexData0[coffset + c] = sliceBytes[c]; + DestroyImmediate(texSliceHDR); + + // L1x + texSliceLDR = new Texture2D(rx, ry, TextureFormat.ARGB32, false); + texSliceLDR.SetPixels(texData1); + texSliceLDR.Apply(); + EditorUtility.CompressTexture(texSliceLDR, TextureFormat.BC7, +#if UNITY_2019_3_OR_NEWER + UnityEditor.TextureCompressionQuality.Best); +#else + (int)UnityEngine.TextureCompressionQuality.Best); +#endif + sliceBytes = texSliceLDR.GetRawTextureData(); + if (compressedSliceSizeLDR == 0) + { + compressedTexData1 = new byte[sliceBytes.Length * rz]; + compressedTexData2 = new byte[sliceBytes.Length * rz]; + compressedTexData3 = new byte[sliceBytes.Length * rz]; + compressedSliceSizeLDR = sliceBytes.Length; + } + coffset = compressedSliceSizeLDR * z; + for(int c=0; c<compressedSliceSizeLDR; c++) compressedTexData1[coffset + c] = sliceBytes[c]; + DestroyImmediate(texSliceLDR); + + // L1y + texSliceLDR = new Texture2D(rx, ry, TextureFormat.ARGB32, false); + texSliceLDR.SetPixels(texData2); + texSliceLDR.Apply(); + EditorUtility.CompressTexture(texSliceLDR, TextureFormat.BC7, +#if UNITY_2019_3_OR_NEWER + UnityEditor.TextureCompressionQuality.Best); +#else + (int)UnityEngine.TextureCompressionQuality.Best); +#endif + sliceBytes = texSliceLDR.GetRawTextureData(); + for(int c=0; c<compressedSliceSizeLDR; c++) compressedTexData2[coffset + c] = sliceBytes[c]; + DestroyImmediate(texSliceLDR); + + // L1z + texSliceLDR = new Texture2D(rx, ry, TextureFormat.ARGB32, false); + texSliceLDR.SetPixels(texData3); + texSliceLDR.Apply(); + EditorUtility.CompressTexture(texSliceLDR, TextureFormat.BC7, +#if UNITY_2019_3_OR_NEWER + UnityEditor.TextureCompressionQuality.Best); +#else + (int)UnityEngine.TextureCompressionQuality.Best); +#endif + sliceBytes = texSliceLDR.GetRawTextureData(); + for(int c=0; c<compressedSliceSizeLDR; c++) compressedTexData3[coffset + c] = sliceBytes[c]; + DestroyImmediate(texSliceLDR); + } + } + else + { + int index = z*ry*rx + y*rx + x; + texData0[index] = new Color(l0r, l0g, l0b, l1zr); + texData1[index] = new Color(l1xr, l1xg, l1xb, l1zg); + texData2[index] = new Color(l1yr, l1yg, l1yb, l1zb); + } + i++; + } + } + } + + var outNameBase = "Assets/" + outputPath + "/" + vol.gameObject.scene.name + "_" + vol.name; + + TextureFormat vformatHDR; + TextureFormat vformatLDR = TextureFormat.BC7; + if (actuallyCompressVolumes) + { + vformatHDR = vol.encoding == BakeryVolume.Encoding.Half4 ? compressedHDRFormat : TextureFormat.BC7; + } + else + { + vformatHDR = vol.encoding == BakeryVolume.Encoding.Half4 ? TextureFormat.RGBAHalf : TextureFormat.ARGB32; + } + + var outName = outNameBase + "0.asset"; + if (File.Exists(outName)) ValidateFileAttribs(outName); + var tex = new Texture3D(rx, ry, rz, vformatHDR, false); + tex.filterMode = FilterMode.Bilinear; + tex.wrapMode = TextureWrapMode.Clamp; + if (actuallyCompressVolumes) + { +#if UNITY_2020_1_OR_NEWER + tex.SetPixelData(compressedTexData0, 0, 0); +#endif + } + else + { + tex.SetPixels(texData0); + } + tex.Apply(); + tex = CreateOrReplaceAsset(tex, outName); + vol.bakedTexture0 = tex; + + outName = outNameBase + "1.asset"; + if (File.Exists(outName)) ValidateFileAttribs(outName); + tex = new Texture3D(rx, ry, rz, actuallyCompressVolumes ? vformatLDR : vformatHDR, false); + tex.filterMode = FilterMode.Bilinear; + tex.wrapMode = TextureWrapMode.Clamp; + if (actuallyCompressVolumes) + { +#if UNITY_2020_1_OR_NEWER + tex.SetPixelData(compressedTexData1, 0, 0); +#endif + } + else + { + tex.SetPixels(texData1); + } + tex.Apply(); + tex = CreateOrReplaceAsset(tex, outName); + vol.bakedTexture1 = tex; + + outName = outNameBase + "2.asset"; + if (File.Exists(outName)) ValidateFileAttribs(outName); + tex = new Texture3D(rx, ry, rz, actuallyCompressVolumes ? vformatLDR : vformatHDR, false); + tex.filterMode = FilterMode.Bilinear; + tex.wrapMode = TextureWrapMode.Clamp; + if (actuallyCompressVolumes) + { +#if UNITY_2020_1_OR_NEWER + tex.SetPixelData(compressedTexData2, 0, 0); +#endif + } + else + { + tex.SetPixels(texData2); + } + tex.Apply(); + tex = CreateOrReplaceAsset(tex, outName); + vol.bakedTexture2 = tex; + + if (actuallyCompressVolumes) + { +#if UNITY_2020_1_OR_NEWER + outName = outNameBase + "3.asset"; + if (File.Exists(outName)) ValidateFileAttribs(outName); + tex = new Texture3D(rx, ry, rz, actuallyCompressVolumes ? vformatLDR : vformatHDR, false); + tex.filterMode = FilterMode.Bilinear; + tex.wrapMode = TextureWrapMode.Clamp; + tex.SetPixelData(compressedTexData3, 0, 0); + tex.Apply(); + tex = CreateOrReplaceAsset(tex, outName); + vol.bakedTexture3 = tex; +#endif + } + else + { + vol.bakedTexture3 = null; + } + + if (shadowmask) + { + var texData = new Color32[numProbes]; + for(int z=0; z<rz; z++) + { + for(int y=0; y<ry; y++) + { + for(int x=0; x<rx; x++) + { + int index = z*ry*rx + y*rx + x; + if (vol.shadowmaskEncoding == BakeryVolume.ShadowmaskEncoding.A8) + { + byte sr = lshadows[maskI*4+0]; + texData[index] = new Color32(sr,sr,sr,sr); + } + else + { + byte sr = lshadows[maskI*4+0]; + byte sg = lshadows[maskI*4+1]; + byte sb = lshadows[maskI*4+2]; + byte sa = lshadows[maskI*4+3]; + texData[index] = new Color32(sr,sg,sb,sa); + } + maskI++; + } + } + } + + + var vformat = (vol.shadowmaskEncoding == BakeryVolume.ShadowmaskEncoding.A8) ? TextureFormat.Alpha8 : TextureFormat.ARGB32; + outName = outNameBase + "_mask.asset"; + if (File.Exists(outName)) ValidateFileAttribs(outName); + tex = new Texture3D(rx, ry, rz, vformat, false); + tex.filterMode = FilterMode.Bilinear; + tex.wrapMode = TextureWrapMode.Clamp; + tex.SetPixels32(texData); + tex.Apply(); + tex = CreateOrReplaceAsset(tex, outName); + vol.bakedMask = tex; + } + + AssetDatabase.SaveAssets(); + + if (vol.isGlobal) vol.OnEnable(); + } + } + } + finally + { + handle.Free(); + handleL1x.Free(); + handleL1y.Free(); + handleL1z.Free(); + if (shadowmask) handleShadows.Free(); + } + } + + static Texture3D CreateOrReplaceAsset(Texture3D src, string path) + { + var dest = AssetDatabase.LoadAssetAtPath<Texture3D>(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<sceneCount; s++) + { + var scene = EditorSceneManager.GetSceneAt(s); + LightingDataAsset copiedAsset = null; + string assetName; + if (!scene.isLoaded) continue; + if (Lightmapping.lightingDataAsset == null) + { + if (emptyLDataAsset == null) emptyLDataAsset = + AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "emptyLightingData.asset", typeof(LightingDataAsset)) as LightingDataAsset; + + if (emptyLDataAsset == null) + { + Debug.LogError("Can't load emptyLightingData.asset"); + continue; + } + + if (copiedAsset == null) + { + assetName = GenerateLightingDataAssetName(); + var outName = "Assets/" + outputPath + "/" + assetName + "_probes.asset"; + if (File.Exists(outName)) ValidateFileAttribs(outName); + if (AssetDatabase.CopyAsset(bakeryRuntimePath + "emptyLightingData.asset", outName)) + { + AssetDatabase.Refresh(); + copiedAsset = AssetDatabase.LoadAssetAtPath(outName, typeof(LightingDataAsset)) as LightingDataAsset; + if (copiedAsset == null) + { + Debug.LogError("Can't load " + outName); + continue; + } + } + else + { + Debug.LogError("Can't copy emptyLightingData.asset"); + continue; + } + } + + Lightmapping.lightingDataAsset = copiedAsset; + } + } + + var bakeFunc = typeof(Lightmapping).GetMethod("BakeAllReflectionProbesSnapshots", + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + if (bakeFunc == null) + { + ProgressBarEnd(); + DebugLogError("Can't get BakeAllReflectionProbesSnapshots function"); + bakeInProgress = false; + yield break; + } + bakeFunc.Invoke(null, null); + + // Revert lighting data assets + /*for(int s=0; s<sceneCount; s++) + { + var scene = EditorSceneManager.GetSceneAt(s); + if (!scene.isLoaded) continue; + if (Lightmapping.lightingDataAsset == emptyLDataAsset) + { + Lightmapping.lightingDataAsset = null; + } + }*/ + + EditorSceneManager.MarkAllScenesDirty(); + + ProgressBarEnd(); + + if (OnFinishedReflectionProbes != null) + { + OnFinishedReflectionProbes.Invoke(this, null); + } + + bakeInProgress = false; + } + + static string GetSunRenderMode(BakeryDirectLight light) + { + return light.cloudShadow != null ? "suncloudshadow" : "sun"; + } + + static string GetPointLightRenderMode(BakeryPointLight light) + { + string renderMode; + if (light.projMode == BakeryPointLight.ftLightProjectionMode.Cone) + { + renderMode = "conelight"; + } + else if (light.projMode == BakeryPointLight.ftLightProjectionMode.Cookie) + { + if (light.cookie == null) + { + Debug.LogError("No cookie texture is set for light " + light.name); + renderMode = "pointlight"; + } + else + { + renderMode = "cookielight"; + } + } + else if (light.projMode == BakeryPointLight.ftLightProjectionMode.Cubemap || light.projMode == BakeryPointLight.ftLightProjectionMode.IES) + { + if (light.projMode == BakeryPointLight.ftLightProjectionMode.Cubemap && light.cubemap == null) + { + Debug.LogError("No cubemap set for light " + light.name); + renderMode = "pointlight"; + } + else if (light.projMode == BakeryPointLight.ftLightProjectionMode.IES && light.iesFile == null) + { + Debug.LogError("No IES file is set for light " + light.name); + renderMode = "pointlight"; + } + else + { + renderMode = "cubemaplight"; + } + } + else + { + renderMode = "pointlight"; + } + return renderMode; + } + + static bool _ValidateCurrentScene() + { + var fname = scenePath + "/lastscene.txt"; + if (!File.Exists(fname)) return false; + var cur = ftRenderLightmap.GenerateLightingDataAssetName(); + var last = File.ReadAllText(fname); + return cur == last; + } + + public static bool ValidateCurrentScene() + { + if (!_ValidateCurrentScene()) + { + DebugLogError("Current scenes don't match last exported scenes - make sure 'Export geometry and maps' is enabled."); + userCanceled = true; + return false; + } + return true; + } + + public static string GenerateLightingDataAssetName() + { + var sceneCount = SceneManager.sceneCount; + var assetName = ""; + var assetNameHashPart = ""; + for(int i=0; i<sceneCount; i++) + { + var s = EditorSceneManager.GetSceneAt(i); + if (!s.isLoaded) continue; + if (i == 0) + { + assetName += s.name; + } + else + { + assetNameHashPart += s.name; + if (i < sceneCount - 1) assetNameHashPart += "__"; + } + } + assetName += "_" + assetNameHashPart.GetHashCode(); + return assetName; + } + + LightingDataAsset ApplyLightingDataAsset(string newPath) + { + var newAsset = AssetDatabase.LoadAssetAtPath(newPath, typeof(LightingDataAsset)) as LightingDataAsset; + int sceneCount = SceneManager.sceneCount; + for(int i=0; i<sceneCount; i++) + { + var s = EditorSceneManager.GetSceneAt(i); + if (!s.isLoaded) continue; + SceneManager.SetActiveScene(s); + Lightmapping.lightingDataAsset = newAsset; + } + return newAsset; + } + +#if UNITY_2017_3_OR_NEWER +#else + Light AddTempShadowmaskLight(Light light, Scene scene) + { + var g = new GameObject(); + SceneManager.MoveGameObjectToScene(g, scene); + var ulht2 = g.AddComponent<Light>(); + 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<long,long> idMap, ref Dictionary<long,int> 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<Light>(); + 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<probeGroups.Length; i++) + { + totalProbes += probeGroups[i].probePositions.Length; + } + if (totalProbes == 0) + { + if (!optional) DebugLogError("Add at least one light probe"); + hasAnyProbes = false; + yield break; + } + } + + ftBuildLights.InitMaps(true); + if (useUnityForOcclsusionProbes) + { + var fgo = GameObject.Find("!ftraceLightmaps"); + if (fgo == null) { + fgo = new GameObject(); + fgo.name = "!ftraceLightmaps"; + fgo.hideFlags = HideFlags.HideInHierarchy; + } + var store = fgo.GetComponent<ftLightmapsStorage>(); + if (store == null) { + store = fgo.AddComponent<ftLightmapsStorage>(); + } + +#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<Renderer>(); + var staticObjectsTerrain = new List<Terrain>(); + var staticObjectsScale = new List<float>(); + var staticObjectsScaleTerrain = new List<float>(); + 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<BakeryLightMesh>(); + //if (areaLight != null && !areaLight.selfShadow) continue; + var mr = ftBuildGraphics.GetValidRenderer(obj); + var mf = obj.GetComponent<MeshFilter>(); + var tr = obj.GetComponent<Terrain>(); + //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<staticObjects.Count; i++) + { + var so = new SerializedObject(staticObjects[i]); + so.FindProperty("m_ScaleInLightmap").floatValue = staticObjectsScale[i]; + so.ApplyModifiedProperties(); + } + for(int i=0; i<staticObjectsTerrain.Count; i++) + { + var so = new SerializedObject(staticObjectsTerrain[i]); + so.FindProperty("m_ScaleInLightmap").floatValue = staticObjectsScaleTerrain[i]; + so.ApplyModifiedProperties(); + } + ftLightmaps.RefreshFull(); + + if (userCanceled) yield break; + } + + int sceneCount = SceneManager.sceneCount; + SceneSetup[] setup = null; + Scene scene; + string lmdataPath = null; + string newPath = null; + newAssetLData = null; +#if UNITY_2017_3_OR_NEWER +#else + Dictionary<long,long> tempID2RealID = null; + Dictionary<long,int> 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<probeGroups.Length; i++) + { + var g = new GameObject(); + g.transform.position = probeGroups[i].transform.position; + g.transform.rotation = probeGroups[i].transform.rotation; + g.transform.localScale = probeGroups[i].transform.lossyScale; + var p = g.AddComponent<LightProbeGroup>(); + 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<Light> maskedLightsTemp = null; + List<Light> maskedLightsReal = null; + if (userRenderMode == RenderMode.Shadowmask || userRenderMode == RenderMode.Subtractive) + { + maskedLightsTemp = new List<Light>(); + maskedLightsReal = new List<Light>(); + AllP = FindObjectsOfType(typeof(BakeryPointLight)) as BakeryPointLight[]; + All3 = FindObjectsOfType(typeof(BakeryDirectLight)) as BakeryDirectLight[]; + for(int i=0; i<All3.Length; i++) + { + var obj = All3[i] as BakeryDirectLight; + if (!obj.enabled) continue; + if (!obj.shadowmask && userRenderMode == RenderMode.Shadowmask) continue; + var ulht = obj.GetComponent<Light>(); + if (ulht == null) continue; + maskedLightsTemp.Add(AddTempShadowmaskLight(ulht, scene)); + maskedLightsReal.Add(ulht); + } + for(int i=0; i<AllP.Length; i++) + { + var obj = AllP[i] as BakeryPointLight; + if (!obj.enabled) continue; + if (!obj.shadowmask && userRenderMode == RenderMode.Shadowmask) continue; + var ulht = obj.GetComponent<Light>(); + 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<long,long>(); + realID2Channel = new Dictionary<long,int>(); + for(int i=0; i<maskedLightsTemp.Count; i++) + { + var lightTemp = maskedLightsTemp[i]; + var lightReal = maskedLightsReal[i]; + if (!GetLightDataForPatching(lightTemp, lightReal, ref tempID2RealID, ref realID2Channel)) + { + hasAnyProbes = false; + DebugLogError("RenderLightProbes error: can't get light IDs"); + RestoreSceneManagerSetup(setup); + yield break; + } + } + } + +#endif + var paths = new string[1]; + paths[0] = tempScenePath; + Lightmapping.BakeMultipleScenes(paths); + while(Lightmapping.isRunning) yield return null; + + var lightingDataAsset = Lightmapping.lightingDataAsset; + if (lightingDataAsset == null) + { + hasAnyProbes = false; + DebugLogError("RenderLightProbes error: lightingDataAsset was not generated"); + RestoreSceneManagerSetup(setup); + ftLightmaps.RefreshFull(); + yield break; + } + lmdataPath = AssetDatabase.GetAssetPath(lightingDataAsset); + newPath = "Assets/" + outputPath + "/" + assetName + "_probes.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 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<Renderer> 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<Renderer>(); + 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<ReflectionProbe>() 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<BakeryDirectLight>(); + var filteredPointLights = new List<BakeryPointLight>(); + 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<BakeryDirectLight>(); + var filteredPointLights = new List<BakeryPointLight>(); + 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<Vector3>[] dirsPerProbe = new List<Vector3>[probes.count]; + List<Vector3>[] dirColorsPerProbe = new List<Vector3>[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<Vector3>(); + dirColorsPerProbe[p] = new List<Vector3>(); + } + 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<Vector3>(); + dirColorsPerProbe[p] = new List<Vector3>(); + } + 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<<layer))!=0) commonFreeBits |= channelBitsPerLayer[layer]; + if (commonFreeBits == fourBits) + { + DebugLogWarning("Light " + ulht.name + " can't generate shadow mask (out of channels)."); + overlappingLights.Add(ulht.gameObject); + return -1; + } + } + + // Get the first available common channel + int firstFreeBit = -1; + for(int bit=0; bit<4; bit++) + { + if ((commonFreeBits & (1<<bit)) == 0) + { + firstFreeBit = bit; + break; + } + } + + foundChannel = firstFreeBit; + } + + // Setup the light + if (!SetupLightShadowmask(ulht, foundChannel)) return -1; + + // Mark the channel as unavailable for affected layers + for(int layer=0; layer<32; layer++) + { + if ((bitmask & (1<<layer))!=0) + { + channelBitsPerLayer[layer] |= 1<<foundChannel; + } + } + + if (shadowmaskGroupID > 0) + { + shadowmaskGroupIDToChannel[shadowmaskGroupID] = foundChannel; + } + + return foundChannel; + } + + static void CollectStorages() + { + var sceneCount = SceneManager.sceneCount; + storages = new Dictionary<Scene, ftLightmapsStorage>(); + for(int i=0; i<sceneCount; i++) + { + var scene = EditorSceneManager.GetSceneAt(i); + if (!scene.isLoaded) continue; + SceneManager.SetActiveScene(scene); + var go = ftLightmaps.FindInScene("!ftraceLightmaps", scene); + if (go == null) { + go = new GameObject(); + go.name = "!ftraceLightmaps"; + go.hideFlags = HideFlags.HideInHierarchy; + } + storage = go.GetComponent<ftLightmapsStorage>(); + if (storage == null) { + storage = go.AddComponent<ftLightmapsStorage>(); + } + storages[scene] = storage; + } + } + + bool CollectGroups(List<BakeryLightmapGroup> groupList, List<BakeryLightmapGroup> groupListGIContributing, bool selected, bool probes=false) + { + // 1: Collect + var sceneCount = SceneManager.sceneCount; + var groups = new List<BakeryLightmapGroup>(); + + // Find explicit LMGroups + var groupsSelectors = FindObjectsOfType(typeof(BakeryLightmapGroupSelector)) as BakeryLightmapGroupSelector[]; + for(int i=0; i<groupsSelectors.Length; i++) + { + var grp = groupsSelectors[i].lmgroupAsset; + if (grp != null && fullSectorRender && (grp as BakeryLightmapGroup).passedFilter != passedFilterFlag) continue; + groups.Add(grp as BakeryLightmapGroup); + } + + // Find implicit LMGroups + for(int s=0; s<sceneCount; s++) + { + var scene = EditorSceneManager.GetSceneAt(s); + if (!scene.isLoaded) continue; + for(int i=0; i<storages[scene].implicitGroups.Count; i++) + { + var grp = storages[scene].implicitGroups[i] as BakeryLightmapGroup; + groups.Add(grp); + } + } + + if (groups==null || groups.Count==0) + { + DebugLogError("Add at least one LMGroup"); + ProgressBarEnd(); + return false; + } + + // 2: Filter + groupListPlain = new List<BakeryLightmapGroupPlain>(); + groupListGIContributingPlain = new List<BakeryLightmapGroupPlain>(); + 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<selObjs.Length; o++) + { + if (selObjs[o] as GameObject == null) continue; + var selGroup = ftBuildGraphics.GetLMGroupFromObject(selObjs[o] as GameObject); + if (selGroup == null) continue; + if (!groupList.Contains(selGroup)) + { + groupList.Add(selGroup); + groupListPlain.Add(selGroup.GetPlainStruct()); + } + } + for(int i=0; i<groups.Count; i++) + { + var lmgroup = groups[i]; + if (lmgroup == null) continue; + if (!groupListGIContributing.Contains(lmgroup)) + { + var outfile = "Assets/" + outputPathFull + "/"+lmgroup.name+"_final.hdr"; + bool exists = File.Exists(outfile); + if ((!exists && lmgroup.mode != BakeryLightmapGroup.ftLMGroupMode.Vertex) && !groupList.Contains(lmgroup)) continue; + groupListGIContributing.Add(lmgroup); + groupListGIContributingPlain.Add(lmgroup.GetPlainStruct()); + } + } + } + else if (probes) + { + // Probes only + for(int i=0; i<groups.Count; i++) + { + var lmgroup = groups[i]; + if (lmgroup == null) continue; + if (groupList.Count == 0 && lmgroup.probes) + { + groupList.Add(lmgroup); + groupListPlain.Add(lmgroup.GetPlainStruct()); + } + if (!groupListGIContributing.Contains(lmgroup)) + { + var outfile = "Assets/" + outputPathFull + "/"+lmgroup.name; + var dirMode = (int)lmgroup.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.Auto ? (int)renderDirMode : (int)lmgroup.renderDirMode; + if (dirMode == (int)RenderDirMode.RNM) + { + outfile += "_RNM0.hdr"; + } + else if (dirMode == (int)RenderDirMode.SH) + { + outfile += "_L0.hdr"; + } + else + { + outfile += "_final.hdr"; + } + bool exists = File.Exists(outfile); + if ((!exists && lmgroup.mode != BakeryLightmapGroup.ftLMGroupMode.Vertex) && !groupList.Contains(lmgroup)) continue; + groupListGIContributing.Add(lmgroup); + groupListGIContributingPlain.Add(lmgroup.GetPlainStruct()); + } + } + if (groupList.Count == 0) + { + DebugLogError("Add at least one LightProbeGroup (L1)"); + ProgressBarEnd(); + return false; + } + } + else + { + // Full render + for(int i=0; i<groups.Count; i++) + { + var lmgroup = groups[i]; + if (lmgroup == null) continue; + if (!groupList.Contains(lmgroup)) + { + groupList.Add(lmgroup); + groupListPlain.Add(lmgroup.GetPlainStruct()); + groupListGIContributing.Add(lmgroup); + groupListGIContributingPlain.Add(lmgroup.GetPlainStruct()); + } + } + } + + return true; + } + + bool ValidateSamples() + { + int warnCount = 0; + int warnLimit = 32; + string warns = ""; + + if (giSamples > 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<All.Length; i++) + { + if (All[i].samples2 > 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<AllP.Length; i++) + { + if (AllP[i].samples > 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<All2.Length; i++) + { + if (All2[i].samples > 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<All3.Length; i++) + { + if (All3[i].samples > 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<BakeryLightmappedPrefab>(); + int pwarnCount = 0; + int pwarnLimit = 32; + string pwarns = ""; + string pwarns2 = ""; + for(int i=0; i<lmprefabs2.Length; i++) + { + var p = lmprefabs2[i]; + if (!p.gameObject.activeInHierarchy) continue; + if (!p.enableBaking) continue; + if (!p.IsValid()) + { + //if (prefabWarning) + { + var warn = p.name + ": " + p.errorMessage; + if (pwarnCount < pwarnLimit) pwarns += warn + "\n"; + DebugLogWarning(warn); + pwarnCount++; + } + } + else + { + lmprefabsList.Add(p); + //if (prefabWarning) + { + if (pwarnCount < pwarnLimit) pwarns2 += p.name + "\n"; + pwarnCount++; + } + } + } + if (pwarnCount > 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<string>(); + ftClient.serverGetFileList = new List<string>(); + } + + // 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<Scene, ftLightmapsStorage>(); + for(int i=0; i<sceneCount; i++) + { + var scene = EditorSceneManager.GetSceneAt(i); + +#if UNITY_2017_3_OR_NEWER + bool mustGenerateLightingDataAsset = false; +#else + bool mustGenerateLightingDataAsset = ((userRenderMode == RenderMode.Shadowmask || userRenderMode == RenderMode.Subtractive) && scene.isDirty); +#endif + if ((unloadScenesInDeferredMode && deferredMode && scene.isDirty) || mustGenerateLightingDataAsset) + { + bool cont = true; + if (verbose) + { + cont = EditorUtility.DisplayDialog("Bakery", "All open scenes must be saved. Save now?", "OK", "Cancel"); + } + if (cont) + { + EditorSceneManager.SaveOpenScenes(); + } + else + { + yield break; + } + } + + if (!scene.isLoaded) continue; + SceneManager.SetActiveScene(scene); + var go = ftLightmaps.FindInScene("!ftraceLightmaps", scene); + if (go == null) { + go = new GameObject(); + go.name = "!ftraceLightmaps"; + go.hideFlags = HideFlags.HideInHierarchy; + } + storage = go.GetComponent<ftLightmapsStorage>(); + if (storage == null) { + storage = go.AddComponent<ftLightmapsStorage>(); + } + + // delete unused sectors from storages + var stSectors = storage.sectors; + if (stSectors == null) stSectors = storage.sectors = new List<ftLightmapsStorage.SectorData>(); + + var newStSectors = new List<ftLightmapsStorage.SectorData>(); + ftLightmapsStorage.SectorData globalSector = null; + for(int st=0; st<stSectors.Count; st++) + { + var stSectorName = stSectors[st].name; + bool isGlobalSector = stSectorName == "$G"; + if (isGlobalSector) globalSector = stSectors[st]; + bool add = false; + if (!isGlobalSector) + { + for(int j=0; j<sectors.Length; j++) + { + if (sectors[j].name == stSectorName) + { + add = true; + break; + } + } + } + if (add) newStSectors.Add(stSectors[st]); + } + if (globalSector == null) + { + globalSector = new ftLightmapsStorage.SectorData(); + globalSector.name = "$G"; + } + newStSectors.Insert(0, globalSector); + + // Cache global bake data as sector for merging + globalSector.maps = storage.maps; + globalSector.masks = storage.masks; + globalSector.dirMaps = storage.dirMaps; + globalSector.rnmMaps0 = storage.rnmMaps0; + globalSector.rnmMaps1 = storage.rnmMaps1; + globalSector.rnmMaps2 = storage.rnmMaps2; + globalSector.mapsMode = storage.mapsMode; + globalSector.bakedRenderers = storage.bakedRenderers; + globalSector.bakedRenderersTerrain = storage.bakedRenderersTerrain; + globalSector.bakedIDs = storage.bakedIDs; + globalSector.bakedIDsTerrain = storage.bakedIDsTerrain; + globalSector.bakedScaleOffset = storage.bakedScaleOffset; + globalSector.bakedScaleOffsetTerrain = storage.bakedScaleOffsetTerrain; + globalSector.bakedVertexColorMesh = storage.bakedVertexColorMesh; + globalSector.nonBakedRenderers = storage.nonBakedRenderers; + + storage.sectors = newStSectors; + + storage.maps = new List<Texture2D>(); + storage.masks = new List<Texture2D>(); + storage.dirMaps = new List<Texture2D>(); + storage.rnmMaps0 = new List<Texture2D>(); + storage.rnmMaps1 = new List<Texture2D>(); + storage.rnmMaps2 = new List<Texture2D>(); + storage.mapsMode = new List<int>(); + storage.bakedLights = new List<Light>(); + storage.bakedLightChannels = new List<int>(); + storage.compressedVolumes = false; + storage.anyVolumes = false; + + //if (forceRefresh) // removed condition to make "Export" option work in isolation + { + storage.serverGetFileList = new List<string>(); + storage.lightmapHasColor = new List<bool>(); + storage.lightmapHasMask = new List<bool>(); + storage.lightmapHasDir = new List<bool>(); + storage.lightmapHasRNM = new List<bool>(); + } + + 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<ReflectionProbe>(); + + //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<string, bool>(); + + 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<BakeryLightmapGroup>(); + var groupListGIContributing = new List<BakeryLightmapGroup>(); + 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<ftSkyLight>(); + var newListB = new List<bool>(); + for(int i=0; i<skylights.Count; i++) + { + if (skylights[i] != null) + { + newList.Add(skylights[i]); + newListB.Add(skylightsDirty[i]); + } + } + storage.skylights = newList; + storage.skylightsDirty = newListB; + */ + + + progressSteps = groupList.Count * (All.Length + AllP.Length + All2.Length + All3.Length) + // direct + 1 + // compositing + bounces * groupList.Count + // GI + groupList.Count * 3; // denoise + fixSeams + encode + progressStepsDone = 0; + + if (deferredMode) + { + deferredCommands = new List<System.Diagnostics.ProcessStartInfo>(); + deferredCommandsFallback = new Dictionary<int, List<string>>(); + deferredCommandsRebake = new Dictionary<int, BakeryLightmapGroupPlain>(); + deferredCommandsLODGen = new Dictionary<int, int>(); + deferredCommandsGIGen = new Dictionary<int, Vector3>(); + deferredCommandsHalf2VB = new Dictionary<int, BakeryLightmapGroupPlain>(); + deferredCommandsUVGB = new Dictionary<int, bool>(); + deferredFileSrc = new List<string>(); + deferredFileDest = new List<string>(); + deferredCommandDesc = new List<string>(); + } + + //if (forceRefresh) // removed condition to make "Export" option work in isolation + { + lightmapMasks = new List<List<List<string>>>(); + lightmapMaskLMNames = new List<List<List<string>>>(); + lightmapMaskLights = new List<List<List<Light>>>(); + lightmapMaskDenoise = new List<List<List<bool>>>(); + #if UNITY_2017_3_OR_NEWER + #else + maskedLights = new List<Light>(); + #endif + lightmapHasColor = new List<bool>(); + lightmapHasMask = new List<bool>(); + lightmapHasDir = new List<bool>(); + lightmapHasRNM = new List<bool>(); + + 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<List<string>>()); + lightmapMaskLMNames.Add(new List<List<string>>()); + lightmapMaskLights.Add(new List<List<Light>>()); + lightmapMaskDenoise.Add(new List<List<bool>>()); + 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<All3.Length; i++) + { + var obj = All3[i] as BakeryDirectLight; + if (!obj.enabled) continue; + var ulht = obj.GetComponent<Light>(); + 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<AllP.Length; i++) + { + var obj = AllP[i] as BakeryPointLight; + if (!obj.enabled) continue; + var ulht = obj.GetComponent<Light>(); + 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<int, int>(); + if (userRenderMode == RenderMode.Shadowmask) + { + overlappingLights = new List<GameObject>(); + + //int channel = 0; + var channelBitsPerLayer = new int[32]; + + for(int i=0; i<All3.Length; i++) + { + var obj = All3[i] as BakeryDirectLight; + if (!obj.enabled) continue; + if (!obj.shadowmask) continue; + var ulht = obj.GetComponent<Light>(); + if (ulht == null) continue; + if (SetupLightShadowmaskUsingBitmask(ulht, obj.bitmask, 0, channelBitsPerLayer) < 0) someLightsCantBeMasked = true; + } + + var lightsRemaining = new List<Light>(); + var lightsRemainingB = new List<BakeryPointLight>(); + var lightChannels = new List<int>(); + var lightArrayIndices = new List<int>(); + var lightIntersections = new List<int>(); + for(int i=0; i<AllP.Length; i++) + { + var obj = AllP[i] as BakeryPointLight; + if (!obj.enabled) continue; + if (!obj.shadowmask) continue; + var ulht = obj.GetComponent<Light>(); + 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<lightsRemaining.Count; i++) + { + lightIntersections[i] = 0; + var la = lightsRemaining[i]; + var laRange = lightsRemainingB[i].cutoff;// * 2; + //var laBounds = new Bounds(la.transform.position, new Vector3(laRange, laRange, laRange)); + var laPos = la.transform.position; + var laBitmask = lightsRemainingB[i].bitmask; + for(int j=0; j<lightsRemaining.Count; j++) + { + if (i == j) continue; + var lb = lightsRemaining[j]; + var lbRange = lightsRemainingB[j].cutoff;// * 2; + var lbPos = lb.transform.position; + var lbBitmask = lightsRemainingB[j].bitmask; + if ((laBitmask & lbBitmask) == 0) continue; + if ((lbPos - laPos).sqrMagnitude < (laRange+lbRange)*(laRange+lbRange)) lightIntersections[i]++; + //var lbBounds = new Bounds(lb.transform.position, new Vector3(lbRange, lbRange, lbRange)); + //if (laBounds.Intersects(lbBounds)) lightIntersections[i]++; + } + } + lightArrayIndices.Sort(delegate(int a, int b) + { + return lightIntersections[b].CompareTo( lightIntersections[a] ); + }); + + for(int i=0; i<lightsRemaining.Count; i++) + { + int idA = lightArrayIndices[i]; + if (lightChannels[idA] != -1) continue; + + var la = lightsRemaining[idA]; + var laRange = lightsRemainingB[idA].cutoff;// * 2; + var laPos = la.transform.position; + //var laBounds = new Bounds(la.transform.position, new Vector3(laRange, laRange, laRange)); + var laBitmask = lightsRemainingB[idA].bitmask; + + var channelBoundsTypeAndOffset = new List<int>(); // sign is type, offset is to relevant array (+1) + // Spherical + var channelBoundsPos = new List<Vector3>(); + var channelBoundsRadius = new List<float>(); + // Convex + var channelBoundsConvex = new List<Convex>(); + + 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<lightsRemaining.Count; j++) + for(int j=0; j<lightsRemaining.Count; j++) + { + int idB = lightArrayIndices[j]; + if (lightChannels[idB] != -1) continue; + var lbBitmask = lightsRemainingB[idB].bitmask; + if ((laBitmask & lbBitmask) == 0) continue; + var lb = lightsRemaining[idB]; + var lbRange = lightsRemainingB[idB].cutoff;// * 2; + //var lbBounds = new Bounds(lb.transform.position, new Vector3(lbRange, lbRange, lbRange)); + var lbT = lb.transform; + var lbPos = lbT.position; + Convex lbConvex = null; + if (lb.type == LightType.Spot) lbConvex = GetSpotConvex(lb); + + bool intersects = false; + int boffset; + for(int k=0; k<channelBoundsTypeAndOffset.Count; k++) + { + boffset = channelBoundsTypeAndOffset[k]; + LightType ctype = LightType.Point; + if (boffset < 0) + { + boffset = -boffset; + ctype = LightType.Spot; + } + boffset--; + + if (lb.type == LightType.Point && ctype == LightType.Point) + { + // sphere vs sphere + //if (channelBounds[k].Intersects(lbBounds)) + float dist = channelBoundsRadius[boffset] + lbRange; + if ((channelBoundsPos[boffset] - lbPos).sqrMagnitude < dist*dist) + { + intersects = true; + break; + } + } + else if (lb.type == LightType.Spot && ctype == LightType.Spot) + { + // convex vs convex + //Debug.Log("testing " + lb.name+" with "+channelBoundsConvex[boffset].vertices[0]); + if (ConvexIntersect(lbConvex, channelBoundsConvex[boffset])) + { + //Debug.LogError(lb.name+" intersects with "+channelBoundsConvex[boffset].vertices[0]); + intersects = true; + break; + } + } + else if (lb.type == LightType.Spot && ctype == LightType.Point) + { + // convex vs sphere + if (ConvexSphereIntersect(lbConvex, channelBoundsPos[boffset], channelBoundsRadius[boffset])) + { + intersects = true; + break; + } + } + else if (lb.type == LightType.Point && ctype == LightType.Spot) + { + // sphere vs convex + if (ConvexSphereIntersect(channelBoundsConvex[boffset], lbPos, lbRange)) + { + intersects = true; + break; + } + } + } + if (intersects) continue; + + if (lb.type == LightType.Spot) + { + channelBoundsTypeAndOffset.Add(-(channelBoundsConvex.Count+1)); + channelBoundsConvex.Add(lbConvex); + } + else + { + // Add point geometry as sphere + channelBoundsTypeAndOffset.Add(channelBoundsPos.Count+1); + channelBoundsPos.Add(lbPos); + channelBoundsRadius.Add(lbRange); + } + //channelBounds.Add(lbBounds); + lightChannels[idB] = channelSet; + DebugLogInfo("Light " + lb.name + " set to channel " + channelSet); + if (!SetupLightShadowmask(lb, channelSet)) someLightsCantBeMasked = true; + } + + //channel++; + } + } + + if (ftAdditionalConfig.batchPointLights) + { + System.Array.Sort(AllP, ComparePointLights); + } + + if (someLightsCantBeMasked) + { + ProgressBarEnd(); + if (verbose) + { + int ch = EditorUtility.DisplayDialogComplex("Bakery", "Some shadow masks can't be baked due to more than 4 masked lights overlapping. See console warnings for details. Press 'Stop and select' to select overlapping lights.", "Continue anyway", "Stop", "Stop and select"); + if (ch > 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<All.Length; i++) + { + var obj = All[i] as BakeryLightMesh; + if (!obj.enabled) continue; + //if ((obj.bitmask & currentGroup.bitmask) == 0) continue; + StoreLight(obj); + } + for(int i=0; i<AllP.Length; i++) + { + var obj = AllP[i] as BakeryPointLight; + if (!obj.enabled) continue; + //if ((obj.bitmask & currentGroup.bitmask) == 0) continue; + StoreLight(obj); + } + for(int i=0; i<All2.Length; i++) + { + var obj = All2[i] as BakerySkyLight; + if (!obj.enabled) continue; + //if ((obj.bitmask & currentGroup.bitmask) == 0) continue; + StoreLight(obj); + } + for(int i=0; i<All3.Length; i++) + { + var obj = All3[i] as BakeryDirectLight; + if (!obj.enabled) continue; + //if ((obj.bitmask & currentGroup.bitmask) == 0) continue; + StoreLight(obj); + } + + foreach(var lmgroup in groupList) + { + // Optionally compute SSS after direct lighting + if (!lmgroup.computeSSS) continue; + RenderLMSSS(lmgroup, bounces == 0); + } + + // Render GI for every lightmap + for(int i=0; i<bounces; i++) + { + // Generate LODs + if (performRendering && giLodModeEnabled) { + foreach(var lmgroup2 in groupListGIContributing) + { + if (lmgroup2.resolution > 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<vols.Length; v++) + { + var vol = vols[v]; + int rx = VolumeDimension(vol.resolutionX); + int ry = VolumeDimension(vol.resolutionY); + int rz = VolumeDimension(vol.resolutionZ); + if (vol.denoise) + { + var progressText = "Denoising volume " + vol.name + "..."; + var 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 = "v "; + startInfo.Arguments += "\"" + scenePath + "/volumes_final_L0" + ext + + "\" \"" + scenePath + "/volumes_final_L1x" + ext + + "\" \"" + scenePath + "/volumes_final_L1y" + ext + + "\" \"" + scenePath + "/volumes_final_L1z" + ext + + "\" " + + voffset + " " + rx + " " + ry + " " + rz + " 32 0"; + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + } + + voffset += rx * ry * rz; + } + + if (clientMode) + { + ftClient.serverGetFileList.Add("volumes_final_L0" + ext); + ftClient.serverGetFileList.Add("volumes_final_L1x" + ext); + ftClient.serverGetFileList.Add("volumes_final_L1y" + ext); + ftClient.serverGetFileList.Add("volumes_final_L1z" + ext); + } + } + } + } + + // Add lightmaps split by buckets + if (ftBuildGraphics.postPacking) + { + foreach(var lmgroup in groupListPlain) + { + //if (lmgroup.parentID != -2) continue; // parent lightmap mark + if (lmgroup.parentName != "|") continue; // parent lightmap mark + var nm = lmgroup.name; + + DebugLogInfo("Preparing (add buckets) lightmap " + nm + " (" + (lmgroup.id+1) + "/" + groupList.Count + ")"); + + var routine = RenderLMAddBuckets(lmgroup.id, nm, lmgroup.resolution, lmgroup.vertexBake, lmgroup.renderDirMode, lmgroup.renderMode); + while(routine.MoveNext()) + { + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + } + } + } + + // Combine masks + 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 (combine masks) lightmap " + nm + " (" + (lmgroup.id+1) + "/" + groupList.Count + ")"); + + var routine = RenderLMCombineMasks(lmgroup.id, nm, lmgroup.resolution, lmgroup.vertexBake, lmgroup.renderMode, lmgroup); + while(routine.MoveNext()) + { + if (userCanceled) + { + ProgressBarEnd(); + yield break; + } + yield return null; + } + } + } + + // Encode lightmaps + foreach(var lmgroup in groupListPlain) + { + if (lmgroup.vertexBake && lmgroup.isImplicit && !lmgroup.probes) continue; // skip objects with scaleImLm == 0 + var nm = lmgroup.name; + bool doRender = true; + + if (lmgroup.parentName != null && lmgroup.parentName.Length > 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<sceneCount; s++) + { + var scene = EditorSceneManager.GetSceneAt(s); + if (!scene.isLoaded) continue; + storage = storages[scene]; + //if (forceRefresh) // removed condition to make "Export" option work in isolation + { + storage.lightmapHasColor = lightmapHasColor; + storage.lightmapHasMask = lightmapHasMask; + storage.lightmapHasDir = lightmapHasDir; + storage.lightmapHasRNM = lightmapHasRNM; + storage.serverGetFileList = ftClient.serverGetFileList; + } + } + EditorSceneManager.MarkAllScenesDirty(); + + // Run commands + if (clientMode) + { + // Add vertex LM data to the list of requested files + var ext = (compressedOutput ? ".lz4" : ".dds"); + foreach(var lmgroup in groupListPlain) + { + 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); + + var lmname = lmgroup.name; + + ftClient.serverGetFileList.Add(lmname + (hasSH ? "_final_L0" : "_final_HDR") + ext); + if (hasShadowMask) ftClient.serverGetFileList.Add(lmname + "_Mask" + ext); + if (hasDir) ftClient.serverGetFileList.Add(lmname + "_final_Dir" + ext); + if (hasSH) + { + ftClient.serverGetFileList.Add(lmname + "_final_L1x" + ext); + ftClient.serverGetFileList.Add(lmname + "_final_L1y" + ext); + ftClient.serverGetFileList.Add(lmname + "_final_L1z" + ext); + } + } + + // Add probe data to requested list + if (lightProbeMode == LightProbeMode.L1 && hasAnyProbes) + { + ftClient.serverGetFileList.Add("probes_final_L0" + ext); + ftClient.serverGetFileList.Add("probes_final_L1x" + ext); + ftClient.serverGetFileList.Add("probes_final_L1y" + ext); + ftClient.serverGetFileList.Add("probes_final_L1z" + ext); + } + + var flist = new BinaryWriter(File.Open(scenePath + "/renderSequence.bin", FileMode.Create)); + byte task; + int tasks = 0; + + flist.Write(tasks); + + tasks++; + flist.Write(ftClient.SERVERTASK_SETSCENENAME); + WriteString(flist, EditorSceneManager.GetActiveScene().name); + + if (deferredCommandsLODGen.Count > 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<lmGroupMinLOD.Length; j++) flist.Write(lmGroupMinLOD[j]); + flist.Write(vbtraceTexPosNormalArray.Count); + for(int j=0; j<vbtraceTexPosNormalArray.Count; j++) flist.Write(vbtraceTexPosNormalArray[j]); + flist.Write(vbtraceTexUVArray.Count); + for(int j=0; j<vbtraceTexUVArray.Count; j++) flist.Write(vbtraceTexUVArray[j]); + flist.Write(vbtraceTexUVArrayLOD.Length); + for(int j=0; j<vbtraceTexUVArrayLOD.Length; j++) flist.Write(vbtraceTexUVArrayLOD[j]); + } + + for(int i=0; i<deferredCommands.Count; i++) + { + if (deferredFileSrc[i].Length > 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<uvSrcBuff.Length; j++) flist.Write(uvSrcBuff[j]); + flist.Write(uvDestBuff.Length); + for(int j=0; j<uvDestBuff.Length; j++) flist.Write(uvDestBuff[j]); + flist.Write(uvBuffOffsets[lmgroup2.id]); + flist.Write(uvBuffLengths[lmgroup2.id]); + flist.Write(lmrIndicesBuff.Length); + for(int j=0; j<lmrIndicesBuff.Length; j++) flist.Write(lmrIndicesBuff[j]); + flist.Write(lmrIndicesOffsets[lmgroup2.id]); + flist.Write(lmrIndicesLengths[lmgroup2.id]); + flist.Write(lmgroup2.resolution/2); + flist.Write(lmgroup2.resolution/2); + flist.Write(lmgroup2.id); + } + } + } + + if (deferredCommandsLODGen.ContainsKey(i)) + { + int id = deferredCommandsLODGen[i]; + tasks++; + flist.Write(ftClient.SERVERTASK_LODGEN); + flist.Write(id); + } + + if (deferredCommandsGIGen.ContainsKey(i)) + { + Vector3 paramz = deferredCommandsGIGen[i]; + int id = (int)paramz.x; + int bounce = (int)paramz.y; + string nm = ""; + for(int j=0; j<groupListPlain.Count; j++) + { + if (groupListPlain[j].id == id) + { + nm = groupListPlain[j].name; + } + } + if (nm.Length == 0) + { + DebugLogError("Error generating GI parameters for ID " + id); + userCanceled = true; + yield break; + } + tasks++; + flist.Write(ftClient.SERVERTASK_GIPARAMS); + WriteString(flist, "gi_" + nm + bounce + ".bin"); + flist.Write(SampleCount(giSamples)); + flist.Write(giBackFaceWeight); + WriteString(flist, bounce == bounces-1 ? "" : "uvalbedo_" + nm + (compressedGBuffer ? ".lz4" : ".dds")); + flist.Write(groupListGIContributingPlain.Count); + flist.Write(giLodModeEnabled ? (byte)1 : (byte)0); + flist.Write(id); + foreach(var lmgroup2 in groupListGIContributingPlain) + { + flist.Write(lmgroup2.id); + WriteString(flist, lmgroup2.name); + flist.Write(compressedOutput ? (byte)1 : (byte)0); + } + WriteString(flist, bounce == bounces - 1 ? (nm + "_lights_Dir" + (compressedOutput ? ".lz4" : ".dds")) : ""); + } + } + + flist.BaseStream.Seek(0, SeekOrigin.Begin); + flist.Write(tasks); + flist.Close(); + + var renderSequence = File.ReadAllBytes(scenePath + "/renderSequence.bin"); + + try + { + if (!ftClient.SendRenderSequence(renderSequence)) + { + DebugLogError("Can't connect to server"); + ProgressBarEnd(); + } + } + catch + { + DebugLogError("Error sending data to server"); + ProgressBarEnd(); + throw; + } + } + else if (deferredMode) + { + DebugLogInfo("Unloading scenes..."); + if (unloadScenesInDeferredMode) UnloadScenes(); + yield return new WaitForEndOfFrame(); + DebugLogInfo("Unloading scenes - done."); + + if (deferredCommands.Count != deferredFileSrc.Count || deferredFileSrc.Count != deferredFileDest.Count || deferredCommands.Count != deferredCommandDesc.Count) + { + DebugLogError("Deferred execution error"); + userCanceled = true; + yield break; + } + + ProgressBarSetStep(1.0f / deferredCommands.Count); + for(int i=0; i<deferredCommands.Count; i++) + { + if (deferredFileSrc[i].Length > 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<groupListPlain.Count; j++) + { + if (groupListPlain[j].id == id) + { + nm = groupListPlain[j].name; + sceneLodLevel = groupListPlain[j].sceneLodLevel; + } + } + if (nm.Length == 0) + { + DebugLogError("Error generating GI parameters for ID " + id); + userCanceled = true; + yield break; + } + GenerateGIParameters(id, nm, bounce, bounces, useDir, sceneLodLevel); + } + + if (deferredCommandsHalf2VB.ContainsKey(i)) + { + var gr = deferredCommandsHalf2VB[i]; + + bool hasShadowMask = gr.renderMode == (int)BakeryLightmapGroup.RenderMode.Shadowmask || + (gr.renderMode == (int)BakeryLightmapGroup.RenderMode.Auto && userRenderMode == RenderMode.Shadowmask); + + bool hasDir = gr.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.DominantDirection || + (gr.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.Auto && renderDirMode == RenderDirMode.DominantDirection); + + bool hasSH = gr.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.SH || + (gr.renderDirMode == (int)BakeryLightmapGroup.RenderDirMode.Auto && renderDirMode == RenderDirMode.SH); + + int err = GenerateVertexBakedMeshes(gr.id, gr.name, hasShadowMask, hasDir, hasSH); + if (err != 0) + { + DebugLogError("Error generating vertex color data for " + gr.name); + userCanceled = true; + yield break; + } + } + + 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(); + } + } + + ProgressBarShow("Finished rendering", 1, true); + + if (unloadScenesInDeferredMode) + { + LoadScenes(); + storages = new Dictionary<Scene, ftLightmapsStorage>(); + //var sanityTimeout = GetTime() + 5; + while( (sceneCount > EditorSceneManager.sceneCount || EditorSceneManager.GetSceneAt(0).path.Length == 0))// && GetTime() < sanityTimeout ) + { + yield return null; + } + for(int i=0; i<sceneCount; i++) + { + var scene = EditorSceneManager.GetSceneAt(i); + if (!scene.isLoaded) continue; + var go = ftLightmaps.FindInScene("!ftraceLightmaps", scene); + storage = go.GetComponent<ftLightmapsStorage>(); + 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<probes.count; i++) + { + var sh = new SphericalHarmonicsL2(); + + sh[r,0] = l0[i*4+0] * 2; + sh[g,0] = l0[i*4+1] * 2; + sh[b,0] = l0[i*4+2] * 2; + + const float convL0 = ftAdditionalConfig.convL0; + const float convL1 = ftAdditionalConfig.convL1; + + // read as BGR (2,1,0) + sh[r,3] = (l1x[i*4+2] * 2.0f - 1.0f) * sh[r,0]*2 * convL1; + sh[g,3] = (l1x[i*4+1] * 2.0f - 1.0f) * sh[g,0]*2 * convL1; + sh[b,3] = (l1x[i*4+0] * 2.0f - 1.0f) * sh[b,0]*2 * convL1; + + sh[r,1] = (l1y[i*4+2] * 2.0f - 1.0f) * sh[r,0]*2 * convL1; + sh[g,1] = (l1y[i*4+1] * 2.0f - 1.0f) * sh[g,0]*2 * convL1; + sh[b,1] = (l1y[i*4+0] * 2.0f - 1.0f) * sh[b,0]*2 * convL1; + + sh[r,2] = (l1z[i*4+2] * 2.0f - 1.0f) * sh[r,0]*2 * convL1; + sh[g,2] = (l1z[i*4+1] * 2.0f - 1.0f) * sh[g,0]*2 * convL1; + sh[b,2] = (l1z[i*4+0] * 2.0f - 1.0f) * sh[b,0]*2 * convL1; + + sh[r,0] *= convL0; + sh[g,0] *= convL0; + sh[b,0] *= convL0; + + shs[i] = sh; + } + } + } + finally + { + handle.Free(); + handleL1x.Free(); + handleL1y.Free(); + handleL1z.Free(); + } + +#if UNITY_2019_3_OR_NEWER + if (useUnityForOcclsusionProbes) + { + // Reload scenes or changes to LightingDataAsset are not applied (?!) + EditorSceneManager.SaveOpenScenes(); + var setup = EditorSceneManager.GetSceneManagerSetup(); + RestoreSceneManagerSetup(setup); + storages = new Dictionary<Scene, ftLightmapsStorage>(); + while( (sceneCount > EditorSceneManager.sceneCount || EditorSceneManager.GetSceneAt(0).path.Length == 0))// && GetTime() < sanityTimeout ) + { + yield return null; + } + for(int i=0; i<sceneCount; i++) + { + var scene = EditorSceneManager.GetSceneAt(i); + if (!scene.isLoaded) continue; + var go = ftLightmaps.FindInScene("!ftraceLightmaps", scene); + storage = go.GetComponent<ftLightmapsStorage>(); + 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<string, Vector2>(); + + // 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<sceneCount; s++) + { + var scene = EditorSceneManager.GetSceneAt(s); + if (!scene.isLoaded) continue; + storage = storages[scene]; + + storage.anyVolumes = hasAnyVolumes; + storage.compressedVolumes = compressVolumes; + + while(storage.maps.Count <= lmgroup.id) + { + storage.maps.Add(null); + } + storage.maps[lmgroup.id] = lm; + + if (userRenderMode == RenderMode.Shadowmask) + { + while(storage.masks.Count <= lmgroup.id) + { + storage.masks.Add(null); + } + storage.masks[lmgroup.id] = mask; + } + + if (dominantDirMode) + { + while(storage.dirMaps.Count <= lmgroup.id) + { + storage.dirMaps.Add(null); + } + storage.dirMaps[lmgroup.id] = dirLightmap; + } + + if (rnmMode || shMode) + { + while(storage.rnmMaps0.Count <= lmgroup.id) + { + storage.rnmMaps0.Add(null); + } + storage.rnmMaps0[lmgroup.id] = rnmLightmap0; + + while(storage.rnmMaps1.Count <= lmgroup.id) + { + storage.rnmMaps1.Add(null); + } + storage.rnmMaps1[lmgroup.id] = rnmLightmap1; + + while(storage.rnmMaps2.Count <= lmgroup.id) + { + storage.rnmMaps2.Add(null); + } + storage.rnmMaps2[lmgroup.id] = rnmLightmap2; + + while(storage.mapsMode.Count <= lmgroup.id) + { + storage.mapsMode.Add(0); + } + storage.mapsMode[lmgroup.id] = rnmMode ? 2 : 3; + } + + // 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]; + } + } + + if (curSector != null && curSectorName.Length > 0) + { + for(int s=0; s<sceneCount; s++) + { + var scene = EditorSceneManager.GetSceneAt(s); + if (!scene.isLoaded) continue; + storage = storages[scene]; + + // Copy lightmap mappings to sector data + if (storage.sectors == null) storage.sectors = new List<ftLightmapsStorage.SectorData>(); + ftLightmapsStorage.SectorData sect = null; + for(int sc=0; sc<storage.sectors.Count; sc++) + { + if (storage.sectors[sc].name == curSectorName) + { + sect = storage.sectors[sc]; + break; + } + } + if (sect == null) + { + sect = new ftLightmapsStorage.SectorData(); + sect.name = curSectorName; + storage.sectors.Add(sect); + } + sect.maps = storage.maps; + sect.masks = storage.masks; + sect.dirMaps = storage.dirMaps; + sect.rnmMaps0 = storage.rnmMaps0; + sect.rnmMaps1 = storage.rnmMaps1; + sect.rnmMaps2 = storage.rnmMaps2; + sect.mapsMode = storage.mapsMode; + sect.bakedRenderers = storage.bakedRenderers; + sect.bakedRenderersTerrain = storage.bakedRenderersTerrain; + sect.bakedIDs = storage.bakedIDs; + sect.bakedIDsTerrain = storage.bakedIDsTerrain; + sect.bakedScaleOffset = storage.bakedScaleOffset; + sect.bakedScaleOffsetTerrain = storage.bakedScaleOffsetTerrain; + sect.bakedVertexColorMesh = storage.bakedVertexColorMesh; + sect.nonBakedRenderers = storage.nonBakedRenderers; + } + } + + if (fullSectorRender || selectedOnly || probesOnlyL1) + { + MergeSectors(); + } + + // Remove unused lightmaps and remap IDs + if (sceneCount > 1 && removeDuplicateLightmaps) + { + for(int s=0; s<sceneCount; s++) + { + var scene = EditorSceneManager.GetSceneAt(s); + if (!scene.isLoaded) continue; + storage = storages[scene]; + var usedIDs = new Dictionary<int, bool>(); + var origID2New = new Dictionary<int, int>(); + for(int i=0; i<storage.bakedIDs.Count; i++) + { + if (storage.bakedIDs[i] < 0 || storage.bakedIDs[i] > storage.maps.Count) continue; + usedIDs[storage.bakedIDs[i]] = true; + } + for(int i=0; i<storage.bakedIDsTerrain.Count; i++) + { + if (storage.bakedIDsTerrain[i] < 0 || storage.bakedIDsTerrain[i] > storage.maps.Count) continue; + usedIDs[storage.bakedIDsTerrain[i]] = true; + } + var newMaps = new List<Texture2D>(); + var newMasks = new List<Texture2D>(); + var newDirMaps = new List<Texture2D>(); + var newRNM0Maps = new List<Texture2D>(); + var newRNM1Maps = new List<Texture2D>(); + var newRNM2Maps = new List<Texture2D>(); + var newMapsMode = new List<int>(); + 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.bakedIDs.Count; i++) + { + int newID = origID2New[storage.bakedIDs[i]]; + if (newID < 0 || newID > storage.maps.Count) continue; + storage.bakedIDs[i] = newID; + } + + for(int i=0; i<storage.bakedIDsTerrain.Count; i++) + { + int newID = origID2New[storage.bakedIDsTerrain[i]]; + if (newID < 0 || newID > 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<lmprefabs.Length; i++) + { + var p = lmprefabs[i]; + if (!p.gameObject.activeInHierarchy) continue; + if (!p.IsValid()) continue; + + var pstoreT = p.transform.Find(bdataName); + if (pstoreT == null) + { + var pstoreG = new GameObject(); + pstoreG.name = bdataName; + pstoreT = pstoreG.transform; + pstoreT.parent = p.transform; + } + var pstore = pstoreT.gameObject.GetComponent<ftLightmapsStorage>(); + if (pstore == null) pstore = pstoreT.gameObject.AddComponent<ftLightmapsStorage>(); + + var prenderers = p.GetComponentsInChildren<Renderer>(); + var pterrains = p.GetComponentsInChildren<Terrain>(); + var plights = p.GetComponentsInChildren<Light>(); + + var storage = storages[p.gameObject.scene]; + + pstore.bakedRenderers = new List<Renderer>(); + pstore.bakedIDs = new List<int>(); + pstore.bakedScaleOffset = new List<Vector4>(); + pstore.bakedVertexColorMesh = new List<Mesh>(); + + pstore.bakedRenderersTerrain = new List<Terrain>(); + pstore.bakedIDsTerrain = new List<int>(); + pstore.bakedScaleOffsetTerrain = new List<Vector4>(); + + pstore.bakedLights = new List<Light>(); + pstore.bakedLightChannels = new List<int>(); + var usedIDs = new Dictionary<int, bool>(); + usedIDs[0] = true; // have to include ID 0 because Unity judges lightmap compression by it + + for(int j=0; j<prenderers.Length; j++) + { + var r = prenderers[j]; + int idx = storage.bakedRenderers.IndexOf(r); + if (idx < 0) continue; + pstore.bakedRenderers.Add(r); + pstore.bakedIDs.Add(storage.bakedIDs[idx]); + pstore.bakedScaleOffset.Add(storage.bakedScaleOffset[idx]); + pstore.bakedVertexColorMesh.Add(storage.bakedVertexColorMesh[idx]); + usedIDs[storage.bakedIDs[idx]] = true; + } + + for(int j=0; j<pterrains.Length; j++) + { + var r = pterrains[j]; + int idx = storage.bakedRenderersTerrain.IndexOf(r); + if (idx < 0) continue; + pstore.bakedRenderersTerrain.Add(r); + pstore.bakedIDsTerrain.Add(storage.bakedIDsTerrain[idx]); + pstore.bakedScaleOffsetTerrain.Add(storage.bakedScaleOffsetTerrain[idx]); + usedIDs[storage.bakedIDsTerrain[idx]] = true; + } + + for(int j=0; j<plights.Length; j++) + { + var r = plights[j]; + int idx = storage.bakedLights.IndexOf(r); + if (idx < 0) continue; + pstore.bakedLights.Add(r); + pstore.bakedLightChannels.Add(storage.bakedLightChannels[idx]); + } + + pstore.maps = new List<Texture2D>(); + pstore.masks = new List<Texture2D>(); + pstore.dirMaps = new List<Texture2D>(); + pstore.rnmMaps0 = new List<Texture2D>(); + pstore.rnmMaps1 = new List<Texture2D>(); + pstore.rnmMaps2 = new List<Texture2D>(); + pstore.mapsMode = new List<int>(); + 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<sceneCount; s++) + { + var scene = EditorSceneManager.GetSceneAt(s); + if (!scene.isLoaded) continue; + storage = storages[scene]; + + // Merge all sectors + var newMaps = new List<Texture2D>(); + var newMasks = new List<Texture2D>(); + var newDirMaps = new List<Texture2D>(); + var newRNM0Maps = new List<Texture2D>(); + var newRNM1Maps = new List<Texture2D>(); + var newRNM2Maps = new List<Texture2D>(); + var newMapsMode = new List<int>(); + var newBakedRenderers = new List<Renderer>(); + var newBakedRenderersTerrain = new List<Terrain>(); + var newBakedIDs = new List<int>(); + var newBakedIDsTerrain = new List<int>(); + var newBakedScaleOffset = new List<Vector4>(); + var newBakedScaleOffsetTerrain = new List<Vector4>(); + var newBakedVertexColorMesh = new List<Mesh>(); + HashSet<Renderer> nonBakedSet = null; + bool anyMasks = false; + bool anyDirMaps = false; + bool anyRNMMaps = false; + int maxMapCount = 0; + for(int sc=0; sc<storage.sectors.Count; sc++) + { + var sect = storage.sectors[sc]; + if (sect.masks != null && sect.masks.Count > 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<Renderer>(); + var terrainSet = new HashSet<Terrain>(); + + 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<storage.sectors.Count; sc++) + { + var sect = storage.sectors[sc]; + bool hasMasks = (sect.masks != null && sect.masks.Count > 0); + bool hasDirMaps = (sect.dirMaps != null && sect.dirMaps.Count > 0); + bool hasRNMMaps = (sect.rnmMaps0 != null && sect.rnmMaps0.Count > 0); + for(int j=0; j<sect.maps.Count; j++) + { + int exists = newMaps.IndexOf(sect.maps[j]); + if (exists >= 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<sect.bakedRenderers.Count; j++) + { + var renderer = sect.bakedRenderers[j]; + if (rendererSet.Contains(renderer)) continue; + rendererSet.Add(renderer); + + newBakedRenderers.Add(renderer); + newBakedScaleOffset.Add(sect.bakedScaleOffset[j]); + newBakedVertexColorMesh.Add(sect.bakedVertexColorMesh.Count > 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<sect.bakedRenderersTerrain.Count; j++) + { + var terrain = sect.bakedRenderersTerrain[j]; + if (terrainSet.Contains(terrain)) continue; + terrainSet.Add(terrain); + + newBakedRenderersTerrain.Add(terrain); + newBakedScaleOffsetTerrain.Add(sect.bakedScaleOffsetTerrain[j]); + + int id = sect.bakedIDsTerrain[j]; + if (id >= 0 && id != 0xFFFF) + { + if (idRemap.Length > id) id = idRemap[id]; + } + newBakedIDsTerrain.Add(id); + } + + if (nonBakedSet == null) + { + nonBakedSet = new HashSet<Renderer>(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<Renderer>(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<BakerySkyLight>(); + if (s != null) s.Start(); + var d = (obj as GameObject).GetComponent<BakeryDirectLight>(); + if (d != null) d.Start(); + var p = (obj as GameObject).GetComponent<BakeryPointLight>(); + if (p != null) p.Start(); + var l = (obj as GameObject).GetComponent<BakeryLightMesh>(); + 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<Transform>().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<Transform>().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<Transform>().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<Transform>().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<Light>(); + 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<Transform>().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<Transform>().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<Light>(); + 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<All.Length; i++) + { + progressStepsDone++; + + var obj = All[i] as BakeryLightMesh; + if (!obj.enabled) continue; + if ((obj.bitmask & currentGroup.bitmask) == 0) continue; + + var lmr = ftBuildGraphics.GetValidRenderer(obj.gameObject); + var lma = obj.GetComponent<Light>(); + 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<AllP.Length; i++) + { + progressStepsDone++; + if (ftAdditionalConfig.batchPointLights) skipLight[i] = true; + + // Cull the light + var obj = AllP[i] as BakeryPointLight; + if (!obj.enabled) continue; + if ((obj.bitmask & currentGroup.bitmask) == 0) continue; + + var boundsRange = obj.cutoff * 2;//obj.GetComponent<Light>().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<All2.Length; i++) + { + progressStepsDone++; + + var obj = All2[i] as BakerySkyLight; + if (!obj.enabled) continue; + if ((obj.bitmask & currentGroup.bitmask) == 0) 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")); + } + } + + 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<All3.Length; i++) + { + progressStepsDone++; + + var obj = All3[i] as BakeryDirectLight; + if (!obj.enabled) continue; + if ((obj.bitmask & currentGroup.bitmask) == 0) continue; + + var lname = GetLightName(obj.gameObject, LMID); + if (doCompose && userRenderMode == RenderMode.Shadowmask && obj.shadowmask) + { + var ulht = obj.GetComponent<Light>(); + 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<atlasTexSize*atlasTexSize*4; f+=4) + { + halfs[f+3] = 15360; // 1.0f in halffloat + } + var posbytes = new byte[atlasTexSize * atlasTexSize * 8]; + System.Buffer.BlockCopy(halfs, 0, posbytes, 0, posbytes.Length); + fpos.Write(posbytes); + fpos.BaseStream.Seek(12, SeekOrigin.Begin); + fpos.Write(atlasTexSize); + fpos.Write(atlasTexSize); + fpos.Close(); + + //yield break; + } + else if (usesIndirectIntensity) + { + cif.fcomp.Seek(0, SeekOrigin.Begin); + cif.fcomp.Write(true); + } + + if (rmode == (int)RenderMode.Shadowmask && cif.fcompIndirect.BaseStream.Position == 0) + { + lightmapHasColor[LMID] = false; + } + + if (!doCompose) + { + progressStepsDone++; + yield break; + } + + progressStepsDone++; + string progressText2 = "Compositing lighting for " + lmname + "..."; + if (!deferredMode) ProgressBarShow(progressText2 , (progressStepsDone / (float)progressSteps), true); + if (userCanceled) + { + 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; + + // Compose + 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(); + if (!performRendering) yield break; + DebugLogInfo("Compositing..."); + + int errCode2 = 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; + + bool shouldAddLights = !(bounces == 0 && (shMode || rnmMode)); + + if (shouldAddLights) + { + if (bounces == 0) + { + startInfo.Arguments = "add " + scenePathQuoted + " \"" + lmname + "_final_HDR" + (compressedOutput ? ".lz4" : ".dds") + + "\" " + PASS_HALF + " " + 0 + " " + LMID + " comp_" + LMID + ".bin"; + } + else + { + startInfo.Arguments = "addmul " + scenePathQuoted + " \"" + lmname + "\" " + PASS_HALF + " " + 0 + " " + LMID + " comp_" + LMID + ".bin";; + } + + deferredFileSrc.Add("");//scenePath + "/comp_" + LMID + ".bin"); + deferredFileDest.Add("");//scenePath + "/comp.bin"); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText2); + } + + if (dominantDirMode)// && rmode == (int)RenderMode.FullLighting) + { + progressText2 = "Compositing direction 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 = "diradd " + scenePathQuoted + " \"" + lmname + (bounces > 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<string>()); + maskArrayLMNames.Add(new List<string>()); + maskArrayLights.Add(new List<Light>()); + maskArrayDenoise.Add(new List<bool>()); + } + 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<string, Vector2>(); + 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<maskNames.Count; c++) + { + var maskNamesOnChannel = maskNames[c]; + var maskLightsOnChannel = maskLights[c]; + + for(int i=0; i<maskNamesOnChannel.Count; i++) + { + var uid = LMID + "_" + c + "_" + i; + + 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 = "postmaskadd " + scenePathQuoted + " \"" + maskNamesOnChannel[i] + + "\"" + " " + PASS_MASK + " " + 0 + " " + LMID + " maskcomp_addbuckets" + uid + ".bin"; + + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add("Compositing masks for " + lmname + "..."); + + var fcomp = new BinaryWriter(File.Open(scenePath + "/maskcomp_addbuckets" + uid + ".bin", FileMode.Create)); + if (clientMode) ftClient.serverFileList.Add("maskcomp_addbuckets" + uid + ".bin"); + fcomp.Write(maskNamesOnChannel[i]); + fcomp.Write("uvnormal_" + lmname + (compressedGBuffer ? ".lz4" : ".dds")); + foreach(var lmgroup2 in groupListPlain) + { + if (lmgroup2.parentName != lmname) continue; + + var maskNames2 = lightmapMasks[lmgroup2.id]; + var maskLMNames2 = lightmapMaskLMNames[lmgroup2.id]; + var maskLights2 = lightmapMaskLights[lmgroup2.id]; + int channels2 = maskNames2.Count; + if (channels2 <= c) continue; + + var names2 = maskNames2[c]; + var lmnames2 = maskLMNames2[c]; + var lights2 = maskLights2[c]; + for(int k=0; k<names2.Count; k++) + { + if (lights2[k] != maskLightsOnChannel[i]) continue; + fcomp.Write(names2[k]); + fcomp.Write("uvnormal_" + lmnames2[k] + (compressedGBuffer ? ".lz4" : ".dds")); + } + } + fcomp.Close(); + } + } + } + } + + yield break; + } + + string GetDenoiseMode() + { + string denoiseMod; + switch(denoiserType) + { + case ftGlobalStorage.DenoiserType.OpenImageDenoise: + denoiseMod = "OIDN"; + break; + case ftGlobalStorage.DenoiserType.Optix5: + denoiseMod = "Legacy"; + break; + case ftGlobalStorage.DenoiserType.Optix7: + denoiseMod = "72"; + break; + default: + denoiseMod = ""; + break; + } + return denoiseMod; + } + + IEnumerator RenderLMCombineMasks(int LMID, string lmname, int resolution, bool vertexBake, int lmgroupRenderMode, BakeryLightmapGroupPlain lmgroup) + { + System.Diagnostics.ProcessStartInfo startInfo; + string progressText; + + //var rmode = lmgroupRenderMode == (int)BakeryLightmapGroup.RenderMode.Auto ? (int)userRenderMode : (int)lmgroupRenderMode; + var denoiseMod = GetDenoiseMode(); + + // Combine shadow masks + if (userRenderMode == RenderMode.Shadowmask) + { + var maskNames = lightmapMasks[LMID]; + var maskLights = lightmapMaskLights[LMID]; + var maskDenoise = lightmapMaskDenoise[LMID]; + + bool process = true; + if (ftBuildGraphics.postPacking) + { + if (lmgroup.parentName != null && lmgroup.parentName.Length > 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<channels2; j++) + { + var names2 = maskNames2[j]; + var lights2 = maskLights2[j]; + var denoise2 = maskDenoise2[j]; + for(int k=0; k<names2.Count; k++) + { + maskNames[j].Add(names2[k]); + maskLights[j].Add(lights2[k]); + maskDenoise[j].Add(denoise2[k]); + } + } + } + } + }*/ + } + + if (maskNames.Count > 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<maskNames.Count; channel++) + { + for(int i=0; i<maskNames[channel].Count; i++) + { + fcomp.Write(maskNames[channel][i]); + if (vertexBake) continue; + if (!maskDenoise[channel][i]) continue; + if (maskLights[channel][i] == null) continue; + + progressText = "Denoising light " + maskLights[channel][i].name + " for shadowmask " + 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 = "m \"" + scenePath + "/" + maskNames[channel][i] + "\" \"" + scenePath + "/" + maskNames[channel][i] + "\""; + string firstArgs = startInfo.Arguments; + startInfo.Arguments += " " + resolution + " 0"; + + if (deferredMode) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + List<string> list; + deferredCommandsFallback[deferredCommands.Count - 1] = list = new List<string>(); + + 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<maskLights.Count; i++) SetupLightShadowmask(maskLights[i], i); + + if (deferredMode) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + if (clientMode && !vertexBake) ftClient.serverGetFileList.Add(lmname + "_mask" + (pstorage.format8bit == BakeryProjectSettings.FileFormat.PNG ? ".png" : ".tga")); + } + else + { + Debug.LogError("Doesn't work in non-deferred mode"); + } + } + } + } + + IEnumerator RenderLMFinalize(int LMID, string lmname, int resolution, bool vertexBake, int lmgroupRenderDirMode, int lmgroupRenderMode, BakeryLightmapGroupPlain lmgroup) + { + System.Diagnostics.ProcessStartInfo startInfo; + //System.Diagnostics.Process exeProcess; + string progressText; + + 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 rmode = lmgroupRenderMode == (int)BakeryLightmapGroup.RenderMode.Auto ? (int)userRenderMode : (int)lmgroupRenderMode; + + var denoiseMod = GetDenoiseMode(); + + // Denoise directions + if (dominantDirMode && denoise && !vertexBake && lightmapHasDir[LMID]) + { + progressText = "Denoising direction 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 = (alternativeDenoiseDir?"D":"d") + " \"" + scenePath + "/" + lmname + "_final_Dir" + (compressedOutput ? ".lz4" : ".dds") + "\" \"" + scenePath + "/" + lmname + "_final_Dir" + (compressedOutput ? ".lz4" : ".dds") + "\""; + string firstArgs = startInfo.Arguments; + startInfo.Arguments += " " + resolution + " 0"; + + if (deferredMode) + { + deferredFileSrc.Add(""); + deferredFileDest.Add(""); + deferredCommands.Add(startInfo); + deferredCommandDesc.Add(progressText); + List<string> list; + deferredCommandsFallback[deferredCommands.Count - 1] = list = new List<string>(); + + 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<string> list; + deferredCommandsFallback[deferredCommands.Count - 1] = list = new List<string>(); + + 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<string> list; + deferredCommandsFallback[deferredCommands.Count - 1] = list = new List<string>(); + + 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<string> list; + deferredCommandsFallback[deferredCommands.Count - 1] = list = new List<string>(); + + 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<GameObject> roots; + public static ftLightmapsStorage FindRenderSettingsStorage() + { + // Load saved settings + GameObject go = null; + if (roots == null) roots = new List<GameObject>(); + + 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<ftLightmapsStorage>(); + if (storage == null) { + storage = go.AddComponent<ftLightmapsStorage>(); + 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<sceneCount; i++) + { + var scene = SceneManager.GetSceneAt(i); + if (!scene.isLoaded) continue; + var go = ftLightmaps.FindInScene("!ftraceLightmaps", scene); + if (go == null) continue; + var store = go.GetComponent<ftLightmapsStorage>(); + if (store == null) continue; + + for(int j=0; j<store.modifiedAssetPathList.Count; j++) + { + bool updated = false; + var path = store.modifiedAssetPathList[j]; + var data = store.modifiedAssets[j]; + int mstoreIndex = gstorage.modifiedAssetPathList.IndexOf(path); + if (mstoreIndex < 0) + { + mstoreIndex = gstorage.modifiedAssetPathList.Count; + gstorage.modifiedAssetPathList.Add(path); + gstorage.modifiedAssets.Add(data); + updated = true; + } + else + { + var dataExisting = gstorage.modifiedAssets[mstoreIndex]; + for(int k=0; k<data.meshName.Count; k++) + { + int ind = dataExisting.meshName.IndexOf( data.meshName[k] ); + if (ind >= 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<string>(); + + for(int i=0; i<sel.Length; i++) + { + var path = AssetDatabase.GetAssetPath(sel[i]); + if (path == "") continue; + if (!pathList.Contains(path)) pathList.Add(path); + } + + int ctr = 0; + for(int i=0; i<pathList.Count; i++) + { + var index = gstorage.modifiedAssetPathList.IndexOf(pathList[i]); + if (index < 0) + { + Debug.Log("UV padding wasn't generated yet, skipping " + pathList[i]); + continue; + } + var mod = gstorage.modifiedAssets[index]; + var asset = ScriptableObject.CreateInstance<ftSavedPadding2>(); + 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<Texture2D> 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<ftLightmapsStorage>(); + if (store == null) { + store = fgo.AddComponent<ftLightmapsStorage>(); + } + 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<Texture2D>(); + int maxLM = 0; + for(int i=0; i<objs.Count; i++) + { + if (objs[i] == null) continue; + objs[i].lightmapScaleOffset = scaleOffset[i]; + if (objs[i].lightmapIndex < 0 || objs[i].lightmapIndex >= 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<sceneCameras.Length; i++) sceneCameras[i].renderingPath = RenderingPath.UsePlayerSettings; + sceneView.SetSceneViewShaderReplace(null, null); + ftLightmaps.RefreshFull(); + enabled = false; + + var gstorage = ftLightmaps.GetGlobalStorage(); + gstorage.checkerPreviewOn = false; + EditorUtility.SetDirty(gstorage); + } + else + { + //if (checkerShader == null) + { + checkerShader = Shader.Find("Hidden/ftChecker"); + if (checkerShader == null) + { + Debug.LogError("Can't load checker shader"); + return; + } + } + sceneView.SetSceneViewShaderReplace(checkerShader, null); + //var sceneCameras = SceneView.GetAllSceneCameras(); + //for(int i=0; i<sceneCameras.Length; i++) sceneCameras[i].renderingPath = RenderingPath.Forward; + enabled = true; + + var gstorage = ftLightmaps.GetGlobalStorage(); + gstorage.checkerPreviewOn = true; + EditorUtility.SetDirty(gstorage); + + Atlas(); + ApplyNewProperties(); + } + sceneView.Repaint(); + } + + //[MenuItem("Bakery/Checker/Refresh")] + public static void RefreshChecker() + { + if (!enabled) return; + Atlas(); + ApplyNewProperties(); + } +} + +//#endif +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSceneView.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSceneView.cs.meta new file mode 100644 index 00000000..d446667c --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSceneView.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 875c029f22e4efd438030561aaaf38b3 +timeCreated: 1540221309 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSectorInspector.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSectorInspector.cs new file mode 100644 index 00000000..1094c929 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSectorInspector.cs @@ -0,0 +1,474 @@ +// Disable 'obsolete' warnings +#pragma warning disable 0618 + +using System.Collections; +using System.Collections.Generic; +using UnityEngine.SceneManagement; +using UnityEngine; +using System.IO; +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.IMGUI.Controls; +using UnityEditor.SceneManagement; +#endif + +#if UNITY_EDITOR +[CustomEditor(typeof(BakerySector))] +public class BakerySectorInspector : Editor +{ + BoxBoundsHandle boundsHandle = new BoxBoundsHandle(typeof(BakerySectorInspector).GetHashCode()); + SerializedProperty ftraceCaptureMode, ftraceCaptureAssetName, ftraceCaptureAsset, ftraceAllowUV; + int curSelectedB = -1; + int curSelectedC = -1; + Tool lastTool = Tool.None; + + static GUIStyle ToggleButtonStyleNormal = null; + static GUIStyle ToggleButtonStyleNormalBig = null; + static GUIStyle CButtonStyle = null; + static GUIStyle XButtonStyle = null; + static GUIStyle LabelStyle = null; + + GameObject objToRemove; + EditorApplication.CallbackFunction remFunc; + + ftLightmapsStorage storage; + + void OnEnable() + { + ftraceCaptureMode = serializedObject.FindProperty("captureMode"); + ftraceCaptureAssetName = serializedObject.FindProperty("captureAssetName"); + ftraceCaptureAsset = serializedObject.FindProperty("captureAsset"); + ftraceAllowUV = serializedObject.FindProperty("allowUVPaddingAdjustment"); + } + + void RemoveWithUndo() + { + EditorApplication.delayCall -= remFunc; + if (objToRemove == null) return; + Undo.DestroyObjectImmediate(objToRemove); + } + + public static void DisablePreview(BakerySector vol) + { + var outRend = vol.previewDisabledRenderers; + if (outRend != null) + { + for(int i=0; i<outRend.Count; i++) + { + if (outRend[i] != null) outRend[i].enabled = true; + } + } + vol.previewDisabledRenderers = null; + + ftRenderLightmap.showProgressBar = false; + ftBuildGraphics.ProgressBarEnd(true); + ftRenderLightmap.showProgressBar = true; + + var temp = vol.previewTempObjects; + if (temp != null) + { + for(int i=0; i<temp.Count; i++) + { + if (temp[i] != null) DestroyImmediate(temp[i]); + } + } + vol.previewTempObjects = null; + + vol.previewEnabled = false; + EditorUtility.SetDirty(vol); + + EditorSceneManager.MarkAllScenesDirty(); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + var vol = target as BakerySector; + + if ( ToggleButtonStyleNormal == null ) + { + ToggleButtonStyleNormal = "Button"; + } + + if ( ToggleButtonStyleNormalBig == null ) + { + ToggleButtonStyleNormalBig = new GUIStyle("Button"); + ToggleButtonStyleNormalBig.fixedHeight = 32; + } + + if (CButtonStyle == null) + { + CButtonStyle = new GUIStyle("Button"); + CButtonStyle.fixedWidth = 48; + } + + if (XButtonStyle == null) + { + XButtonStyle = new GUIStyle("Button"); + XButtonStyle.fixedWidth = 32; + } + + if (LabelStyle == null) + { + LabelStyle = new GUIStyle("Label"); + LabelStyle.fontSize = 18; + LabelStyle.fontStyle = FontStyle.Bold; + } + + if (remFunc == null) remFunc = new EditorApplication.CallbackFunction(RemoveWithUndo); + + EditorGUILayout.PropertyField(ftraceAllowUV, new GUIContent("Allow UV adjustment", "Allow UV padding adjustment when baking this sector? Disable when having multiple sectors affecting instances of the same mesh to prevent one sector from breaking UVs on another sector.")); + + if (vol.previewEnabled) GUI.enabled = false; + EditorGUILayout.PropertyField(ftraceCaptureMode, new GUIContent("Capture mode", "'Capture In Place' will generate outside geometry approximation every time 'Render' is pressed or RTPreview is open. It is a good option for exterior scenes where all sectors are loaded together and visible in the Editor.\n'Capture To Asset' will save approximated outside geometry into a file which can be used in another scene using 'Load Captured'.")); + + if (ftraceCaptureMode.intValue == (int)BakerySector.CaptureMode.CaptureToAsset) + { + EditorGUILayout.Space(); + var assetName = ftraceCaptureAssetName.stringValue; + if (assetName.Length == 0) assetName = "SectorCapture_" + target.name; + assetName = EditorGUILayout.TextField("Asset name", assetName); + bool guiPrev = GUI.enabled; + GUI.enabled = false; + EditorGUILayout.PropertyField(ftraceCaptureAsset, new GUIContent("Captured asset", "")); + GUI.enabled = guiPrev; + EditorGUILayout.Space(); + if (GUILayout.Button("Capture", GUILayout.Height(32))) + { + if (storage == null) storage = ftRenderLightmap.FindRenderSettingsStorage(); + + var asset = ScriptableObject.CreateInstance<BakerySectorCapture>(); + 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.meshes.Count; i++) + { + if (asset.meshes[i] == null) + { + Debug.LogError("Mesh " + i + " is null"); + continue; + } + AssetDatabase.AddObjectToAsset(asset.meshes[i], apath); + AssetDatabase.AddObjectToAsset(asset.textures[i], apath); + } + + AssetDatabase.SaveAssets(); + ftraceCaptureAsset.objectReferenceValue = asset; + } + else + { + Debug.LogError("SectorCapture wasn't generated"); + } + ftBuildGraphics.ProgressBarEnd(true); + } + EditorGUILayout.Space(); + } + else if (ftraceCaptureMode.intValue == (int)BakerySector.CaptureMode.LoadCaptured) + { + EditorGUILayout.Space(); + EditorGUILayout.PropertyField(ftraceCaptureAsset, new GUIContent("Captured asset", "")); + } + + if (vol.previewEnabled) GUI.enabled = true; + + EditorGUILayout.Space(); + + bool loadNothing = (ftraceCaptureMode.intValue == (int)BakerySector.CaptureMode.LoadCaptured && ftraceCaptureAsset.objectReferenceValue == null); + if (loadNothing) GUI.enabled = false; + + bool previewEnabled = GUILayout.Toggle(vol.previewEnabled, "Preview", ToggleButtonStyleNormalBig); + if (!vol.previewEnabled && previewEnabled) + { + vol.previewEnabled = true; + + if (storage == null) storage = ftRenderLightmap.FindRenderSettingsStorage(); + + BakerySectorCapture asset = null; + bool loadedAsset = (ftraceCaptureMode.intValue == (int)BakerySector.CaptureMode.LoadCaptured); + + if (loadedAsset) + { + asset = vol.captureAsset; + asset.write = false; + } + else + { + asset = ScriptableObject.CreateInstance<BakerySectorCapture>(); + 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<outRend.Count; i++) + { + if (outRend[i] != null) outRend[i].enabled = false; + } + } + + vol.previewTempObjects = ftBuildGraphics.temporaryGameObjects; + + EditorUtility.SetDirty(vol); + if (!loadedAsset) DestroyImmediate(asset); + + EditorSceneManager.MarkAllScenesDirty(); + } + else if (vol.previewEnabled && !previewEnabled) + { + DisablePreview(vol); + } + if (loadNothing) GUI.enabled = true; + + EditorGUILayout.Space(); + EditorGUILayout.BeginVertical("box"); + + if (previewEnabled) GUI.enabled = false; + + if (GUILayout.Button(new GUIContent("Add capture point", "Adds a new capture point to this sector. Points will appear as dummy objects parented to this object. When baking the scene (or clicking 'Capture'), each point will generate a simplified scene representation as seen from it. Points can approximate parts of the outside scene geometry and provide shadows/bounces from that geometry without loading the whole world in memory."))) + { + var g = new GameObject(); + Undo.RegisterCreatedObjectUndo(g, "Create capture point"); + g.name = vol.name + "_C_" + vol.tforms.Count; + var t = g.transform; + t.localPosition = vol.transform.position; + t.parent = vol.transform; + t.localScale = Vector3.one * 4; + vol.cpoints.Add(t); + } + + EditorGUILayout.Space(); + + if (vol.cpoints.Count > 0) + { + GUILayout.Label("Edit capture points:"); + } + + for(int i=0; i<vol.cpoints.Count; i++) + { + if (vol.cpoints[i] == null) + { + vol.cpoints.RemoveAt(i); + curSelectedC = -1; + break; + } + + GUILayout.BeginHorizontal("box"); + + bool wasSelected = i == curSelectedC; + bool selected = GUILayout.Toggle(i == curSelectedC, new GUIContent("" + i, "Select this capture point. Switch to the Move tool to manipulate it."), ToggleButtonStyleNormal); + if (selected) + { + curSelectedC = i; + curSelectedB = -1; + } + else if (wasSelected != selected) + { + curSelectedC = -1; + } + + if (GUILayout.Button("Clone", CButtonStyle)) + { + var g = new GameObject(); + Undo.RegisterCreatedObjectUndo(g, "Clone capture point"); + g.name = vol.name + "_C_" + vol.cpoints.Count; + var t = g.transform; + t.localPosition = vol.cpoints[i].position; + t.parent = vol.transform; + t.localScale = Vector3.one * 4; + vol.cpoints.Add(t); + } + + if (GUILayout.Button(new GUIContent("X", "Delete this capture point"), XButtonStyle)) + { + objToRemove = vol.cpoints[i].gameObject; + + Undo.RecordObject(vol, "Remove capture point"); + vol.cpoints.RemoveAt(i); + curSelectedC = -1; + + EditorApplication.delayCall += remFunc; + + break; + } + GUILayout.EndHorizontal(); + } + + EditorGUILayout.EndVertical(); + + if (previewEnabled) GUI.enabled = true; + + serializedObject.ApplyModifiedProperties(); + } + + protected virtual void OnSceneGUI() + { + var vol = (BakerySector)target; + + var origHMatrix = Handles.matrix; + boundsHandle.center = Vector3.zero; + boundsHandle.size = Vector3.one; + + var solid = new Color(0.3f, 0.6f, 0.95f) * 2; + //var semiTransparent = new Color(1, 1, 1, 0.2f); + Handles.color = solid; + + if (Tools.current != lastTool && Tools.current != Tool.None) + { + lastTool = Tools.current; + } + if (curSelectedB >= 0 || curSelectedC >= 0) Tools.current = Tool.None; + + for(int i=0; i<vol.tforms.Count; i++) + { + if (vol.tforms[i] == null) continue; + + Handles.matrix = origHMatrix; + //Handles.color = solid; + + Handles.zTest = UnityEngine.Rendering.CompareFunction.Less; + Handles.matrix = Matrix4x4.TRS(vol.tforms[i].position, vol.tforms[i].rotation, Vector3.one); + boundsHandle.size = vol.tforms[i].localScale; + + Handles.Label(Vector3.zero, "" + i, LabelStyle); + + if (!vol.previewEnabled) + { + EditorGUI.BeginChangeCheck(); + boundsHandle.DrawHandle(); + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(vol.tforms[i], "Change Bounds"); + vol.tforms[i].localScale = boundsHandle.size; + vol.tforms[i].position = Handles.matrix.MultiplyPoint(boundsHandle.center); + } + } + + //Handles.color = semiTransparent; + //Handles.DrawWireCube(boundsHandle.center, boundsHandle.size + Vector3.one * vol.nearDistance); + } + + if (curSelectedB >= 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<vol.cpoints.Count; i++) + { + if (vol.cpoints[i] == null) continue; + + Handles.zTest = UnityEngine.Rendering.CompareFunction.Less; + + try + { + Handles.Label(vol.cpoints[i].position, "" + i, LabelStyle); + } + catch + { + // Unity can throw nullrefs when Handles.Label uses larger font + } + } + + if (curSelectedC >= 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 = "//<FTRACEV1.0>"; + string ftSignatureBicubic = "//<FTRACE_BICUBIC>"; + string ftSignatureShadowmask = "//<FTRACE_SHADOWMASK>"; + string ftSignatureEnd = "//</FTRACEV1.0>"; + 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<string>(); + 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.Count; i++) + { + writer.WriteLine(lines[i]); + } + writer.Close(); + //EditorUtility.DisplayDialog("Bakery", "Restart Editor to apply changes", "OK"); + return true; + } + + void OnGUI() + { + if (!initialized) + { + try + { + bicubic = false; + var entryAssembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly; + var managedDir = System.IO.Path.GetDirectoryName(entryAssembly.Location); + shadersDir = managedDir + "/../CGIncludes/"; + if (!Directory.Exists(shadersDir)) shadersDir = managedDir + "/../../CGIncludes/"; + if (!Directory.Exists(shadersDir)) + { + UnityEngine.Debug.LogError("Can't find directory: " + shadersDir); + return; + } + + includeGIPath = shadersDir + "UnityGlobalIllumination.cginc"; + if (File.Exists(includeGIPath)) + { + var reader = new StreamReader(includeGIPath); + if (reader == null) + { + UnityEngine.Debug.LogError("Can't open " + includeGIPath); + bicubic = false; + return; + } + //bool patched = false; + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line.StartsWith(ftSignatureBegin)) + { + UnityEngine.Debug.Log("Bicubic: already patched"); + //patched = true; + bicubic = true; + break; + } + } + reader.Close(); + } + + shadowBlend = false; + includeShadowPath = shadersDir + "UnityShadowLibrary.cginc"; + if (File.Exists(includeShadowPath)) + { + var reader = new StreamReader(includeShadowPath); + if (reader == null) + { + UnityEngine.Debug.LogError("Can't open " + includeShadowPath); + bicubicShadow = false; + return; + } + //bool patched = false; + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line.StartsWith(ftSignatureShadowmask)) + { + UnityEngine.Debug.Log("Shadowmask: already patched"); + //patched = true; + shadowBlend = true; + break; + } + } + reader.Close(); + } + + falloff = false; + includeLightPath = shadersDir + "AutoLight.cginc"; + if (File.Exists(includeLightPath)) + { + var reader = new StreamReader(includeLightPath); + if (reader == null) + { + UnityEngine.Debug.LogError("Can't open " + includeLightPath); + falloff = false; + return; + } + //bool patched = false; + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line.StartsWith(ftSignatureBegin)) + { + UnityEngine.Debug.Log("Lights: already patched"); + //patched = true; + falloff = true; + break; + } + } + reader.Close(); + } + falloffDeferred = false; + includeDeferredPath = shadersDir + "UnityDeferredLibrary.cginc"; + if (File.Exists(includeDeferredPath)) + { + var reader = new StreamReader(includeDeferredPath); + if (reader == null) + { + UnityEngine.Debug.LogError("Can't open " + includeDeferredPath); + falloffDeferred = false; + return; + } + //bool patched = false; + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line.StartsWith(ftSignatureBegin)) + { + UnityEngine.Debug.Log("Lights: already patched"); + //patched = true; + falloffDeferred = true; + break; + } + } + reader.Close(); + } + initialized = true; + } + catch//(System.UnauthorizedAccessException err) + { + GUI.Label(new Rect(10, 20, 320, 60), "Can't access Unity shader include files,\ntry running Unity as admin."); + return; + } + } + + bool wasBicubic = bicubic; + //bool wasBicubicShadow = bicubicShadow; + bool wasShadowBlend = shadowBlend; + bool wasFalloff = falloff; + bool wasFalloffDeferred = falloffDeferred; + + this.minSize = new Vector2(320, 290+60); + + GUI.Label(new Rect(10, 20, 320, 60), "These settings will modify base Unity shaders.\nAll projects opened with this version of Editor\nwill use modified shaders."); + //agree = GUI.Toggle(new Rect(10, 65, 200, 15), agree, "I understand"); + + GUI.BeginGroup(new Rect(10, 80, 300, 260), "Options"); + if (initialized) + { + bicubic = GUI.Toggle(new Rect(10, 20, 280, 50), bicubic, "Use bicubic interpolation for lightmaps", "Button"); + shadowBlend = GUI.Toggle(new Rect(10, 80, 280, 50), shadowBlend, "Use multiplication for shadowmask", "Button"); + falloff = GUI.Toggle(new Rect(10, 140, 280, 50), falloff, "Use physical light falloff (Forward)", "Button"); + falloffDeferred = GUI.Toggle(new Rect(10, 200, 280, 50), falloffDeferred, "Use physical light falloff (Deferred)", "Button"); + + if (!wasBicubic && bicubic) + { + CopyInclude(shadersDir); + var reader = new StreamReader(includeGIPath); + if (reader == null) + { + UnityEngine.Debug.LogError("Can't open " + includeGIPath); + bicubic = false; + return; + } + bool patched = false; + + var lines = new List<string>(); + 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.Count; i++) + { + writer.WriteLine(lines[i]); + } + writer.Close(); + //EditorUtility.DisplayDialog("Bakery", "Restart Editor to apply changes", "OK"); + } + + reader = new StreamReader(includeShadowPath); + if (reader == null) + { + UnityEngine.Debug.LogError("Can't open " + includeShadowPath); + bicubic = false; + return; + } + patched = false; + + lines = new List<string>(); + 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.Count; i++) + { + writer.WriteLine(lines[i]); + } + writer.Close(); + EditorUtility.DisplayDialog("Bakery", "Restart Editor to apply changes", "OK"); + } + } + + if (wasBicubic && !bicubic) + { + bicubic = true; + if (RevertFile(includeGIPath)) bicubic = false; + bicubicShadow = true; + if (RevertFile(includeShadowPath)) + { + bicubicShadow = false; + shadowBlend = false; + } + EditorUtility.DisplayDialog("Bakery", "Restart Editor to apply changes", "OK"); + } + + if (!wasShadowBlend && shadowBlend) + { + CopyInclude(shadersDir); + var reader = new StreamReader(includeShadowPath); + if (reader == null) + { + UnityEngine.Debug.LogError("Can't open " + includeShadowPath); + shadowBlend = false; + return; + } + bool patched = false; + + var lines = new List<string>(); + 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.Count; i++) + { + writer.WriteLine(lines[i]); + } + writer.Close(); + EditorUtility.DisplayDialog("Bakery", "Restart Editor to apply changes", "OK"); + } + } + + if (wasShadowBlend && !shadowBlend) + { + shadowBlend = true; + if (RevertFile(includeShadowPath)) shadowBlend = false; + + bicubic = true; + if (RevertFile(includeGIPath)) bicubic = false; + + EditorUtility.DisplayDialog("Bakery", "Restart Editor to apply changes", "OK"); + } + + if (!wasFalloff && falloff) + { + CopyInclude(shadersDir); + var reader = new StreamReader(includeLightPath); + if (reader == null) + { + UnityEngine.Debug.LogError("Can't open " + includeLightPath); + falloff = false; + return; + } + bool patched = false; + + var lines = new List<string>(); + 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.Count; i++) + { + writer.WriteLine(lines[i]); + } + writer.Close(); + EditorUtility.DisplayDialog("Bakery", "Restart Editor to apply changes", "OK"); + } + } + + if (wasFalloff && !falloff) + { + falloff = true; + if (RevertFile(includeLightPath)) falloff = false; + EditorUtility.DisplayDialog("Bakery", "Restart Editor to apply changes", "OK"); + } + + + if (!wasFalloffDeferred && falloffDeferred) + { + CopyInclude(shadersDir); + var reader = new StreamReader(includeDeferredPath); + if (reader == null) + { + UnityEngine.Debug.LogError("Can't open " + includeDeferredPath); + falloff = false; + return; + } + bool patched = false; + + var lines = new List<string>(); + 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<lines.Count; i++) + { + writer.WriteLine(lines[i]); + } + writer.Close(); + EditorUtility.DisplayDialog("Bakery", "Restart Editor to apply changes", "OK"); + } + } + + if (wasFalloffDeferred && !falloffDeferred) + { + falloffDeferred = true; + if (RevertFile(includeDeferredPath)) falloffDeferred = false; + EditorUtility.DisplayDialog("Bakery", "Restart Editor to apply changes", "OK"); + } + + + } + else + { + GUI.Label(new Rect(10, 20, 250, 30), "Can't find Unity include at path: \n" + includeGIPath + "."); + } + GUI.EndGroup(); + } + + [MenuItem ("Bakery/Global shader tweaks", false, 60)] + public static void RenderLightmap () { + ScriptableWizard.DisplayWizard("Bakery - shader tweaks", typeof(ftShaderTweaks), "RenderLightmap"); + } +} + +#endif diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftShaderTweaks.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftShaderTweaks.cs.meta new file mode 100644 index 00000000..2d555474 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftShaderTweaks.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 482c0edd4bdba214f93b66b9cf3c0f3e +timeCreated: 1527024891 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSkyLightInspector.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSkyLightInspector.cs new file mode 100644 index 00000000..c481db32 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/ftSkyLightInspector.cs @@ -0,0 +1,360 @@ + +using UnityEditor; +using UnityEngine; +using System; +using System.IO; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEditor.SceneManagement; +using UnityEngine.SceneManagement; + +[CustomEditor(typeof(BakerySkyLight))] +[CanEditMultipleObjects] +public class ftSkyLightInspector : UnityEditor.Editor +{ + public static Quaternion QuaternionFromMatrix(Matrix4x4 m) { + Quaternion q = new Quaternion(); + q.w = Mathf.Sqrt( Mathf.Max( 0, 1 + m[0,0] + m[1,1] + m[2,2] ) ) / 2; + q.x = Mathf.Sqrt( Mathf.Max( 0, 1 + m[0,0] - m[1,1] - m[2,2] ) ) / 2; + q.y = Mathf.Sqrt( Mathf.Max( 0, 1 - m[0,0] + m[1,1] - m[2,2] ) ) / 2; + q.z = Mathf.Sqrt( Mathf.Max( 0, 1 - m[0,0] - m[1,1] + m[2,2] ) ) / 2; + q.x *= Mathf.Sign( q.x * ( m[2,1] - m[1,2] ) ); + q.y *= Mathf.Sign( q.y * ( m[0,2] - m[2,0] ) ); + q.z *= Mathf.Sign( q.z * ( m[1,0] - m[0,1] ) ); + return q; + } + + SerializedProperty ftraceLightColor; + SerializedProperty ftraceLightIntensity; + SerializedProperty ftraceLightTexture; + SerializedProperty ftraceLightSamples; + SerializedProperty ftraceLightHemi; + SerializedProperty ftraceLightCorrectRot; + SerializedProperty ftraceLightBitmask; + SerializedProperty ftraceLightBakeToIndirect; + SerializedProperty ftraceLightIndirectIntensity; + SerializedProperty ftraceTangentSH; + + int texCached = -1; + + void TestPreviewRefreshProperty(ref int cached, int newVal) + { + if (cached >= 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<string, Vector2> texSettings = new Dictionary<string, Vector2>(); + 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<Camera>(); + 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<uvs.Length; i++) + { + pos[i] = new Vector3(uvs[i].x * scaleOffset.x + scaleOffset.z, uvs[i].y * scaleOffset.y + scaleOffset.w, 0.0f); + } + m.vertices = pos;*/ + + m.uv2 = uvOverride; + + if (vertexBake) + { + for(int i=0; i<mesh.subMeshCount; i++) + { + var indices = m.GetIndices(i); + m.SetIndices(indices, MeshTopology.Points, i, false); + } + } + } + + var scaleOffsetFlipped = new Vector4(scaleOffset.x, -scaleOffset.y, scaleOffset.z, 1.0f - scaleOffset.w); + + //UpdateMatrix(worldMatrix); + + for(int pass=0; pass<PASS_COUNT; pass++) + { + if (pass == PASS_EMISSIVE && !emissiveEnabled) continue; + if (pass == PASS_NORMAL && !normalEnabled) continue; + if (pass == PASS_ALPHA && !alphaEnabled) continue; // per Start-End + if (pass == PASS_ALPHA && !metaAlpha) continue; // per this object + + if (pass == PASS_ALBEDO) + { + Graphics.SetRenderTarget(rtAlbedo); + } + else if (pass == PASS_EMISSIVE) + { + Graphics.SetRenderTarget(rtEmissive); + } + else if (pass == PASS_NORMAL) + { + Graphics.SetRenderTarget(rtNormal); + } + else if (pass == PASS_ALPHA) + { + Graphics.SetRenderTarget(rtAlpha); + } + + for(int i=0; i<mesh.subMeshCount; i++) + { + if (materials.Length <= i) break; + if (materials[i] == null) continue; + if (materials[i].shader == null) continue; + + // Optionally skip emission + bool passAsBlack = (pass == PASS_EMISSIVE && materials[i].globalIlluminationFlags != MaterialGlobalIlluminationFlags.BakedEmissive); + + var rpTag = materials[i].GetTag("RenderPipeline", true, ""); + bool isHDRP = rpTag == "HDRenderPipeline"; + if (pass >= 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<materials[i].passCount; mpass++) + { + if (materials[i].GetPassName(mpass).IndexOf("META") >= 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<uvOffset.Length/2; j++) + { + if (pass < PASS_NORMAL) + { + UpdateMatrix(worldMatrix, uvOffset[j*2] * texelSize, uvOffset[j*2+1] * texelSize); + } + else + { + // TODO: use in HDRP as well + var srcVec = (isHDRP) ? scaleOffsetFlipped : scaleOffset; + var vec = new Vector4(srcVec.x, srcVec.y, srcVec.z + uvOffset[j*2] * texelSize, srcVec.w + uvOffset[j*2+1] * texelSize); + Shader.SetGlobalVector("unity_LightmapST", vec); + if (bakeryPass >= 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<MeshRenderer>() as MeshRenderer[]; + if (mrs.Length > 0) + { + var b = mrs[0].bounds; + for(int i=1; i<mrs.Length; i++) + { + b.Encapsulate(mrs[i].bounds); + } + Undo.RecordObject(vol, "Change Bounds"); + vol.transform.position = b.center; + vol.bounds = b; + Debug.Log("Bounds set"); + } + else + { + Debug.LogError("No mesh renderers to wrap to"); + } + } + + var boxCol = vol.GetComponent<BoxCollider>(); + 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 <icastano@nvidia.com> + +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<Vector2> newUVBuffer; + public static List<int> 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<T>(List<int> xrefArray, T[] origArray) + { + if (origArray == null || origArray.Length == 0) return origArray; + + var arr = new T[xrefArray.Count]; + for(int i=0; i<xrefArray.Count; i++) + { + int xref = xrefArray[i]; + arr[i] = origArray[xref]; + } + return arr; + + /* + var finalAttr = new T[vertCount + xrefCount]; + for(int i=0; i<vertCount; i++) finalAttr[i] = origArray[i]; + for(int i=0; i<xrefCount; i++) finalAttr[i + vertCount] = origArray[ xrefArray[i] ]; + return finalAttr; + */ + } + + public static double GetTime() + { + return (System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond) / 1000.0; + } + + public static void Unwrap(Mesh m, UnwrapParam uparams) + { + //EditorUtility.DisplayDialog("Bakery", "xatlas start", "OK"); + int padding = (int)(uparams.packMargin*1024); + //Debug.Log("xatlas! " + padding); + + newUVBuffer = null; + newXrefBuffer = null; + + var t = GetTime(); + + var positions = m.vertices; + var normals = m.normals; + var existingUV = m.uv; + var handlePos = GCHandle.Alloc(positions, GCHandleType.Pinned); + var handleNorm = GCHandle.Alloc(normals, GCHandleType.Pinned); + var handleUV = GCHandle.Alloc(existingUV, GCHandleType.Pinned); + int err = 0; + + var atlas = xatlasCreateAtlas(); + + //EditorUtility.DisplayDialog("Bakery", "xatlas created", "OK"); + + try + { + var pointerPos = handlePos.AddrOfPinnedObject(); + var pointerNorm = handleNorm.AddrOfPinnedObject(); + +#if UV_HINT + var pointerUV = handleUV.AddrOfPinnedObject(); +#else + var pointerUV = (System.IntPtr)0; +#endif + + for(int i=0; i<m.subMeshCount; i++) + { + err = xatlasAddMesh(atlas, m.vertexCount, pointerPos, pointerNorm, pointerUV, (int)m.GetIndexCount(i), m.GetIndices(i)); + if (err == 1) + { + Debug.LogError("xatlas::AddMesh: indices are out of range"); + } + else if (err == 2) + { + Debug.LogError("xatlas::AddMesh: index count is incorrect"); + } + else if (err != 0) + { + Debug.LogError("xatlas::AddMesh: unknown error"); + } + if (err != 0) break; + } + //EditorUtility.DisplayDialog("Bakery", "xatlas added", "OK"); + if (err == 0) + { + xatlasParametrize(atlas); + //EditorUtility.DisplayDialog("Bakery", "xatlas param done", "OK"); + + xatlasPack(atlas, 4096, 0, 0, 1024, padding, false, true);//, true); + //EditorUtility.DisplayDialog("Bakery", "xatlas pack done", "OK"); + } + } + finally + { + if (handlePos.IsAllocated) handlePos.Free(); + if (handleNorm.IsAllocated) handleNorm.Free(); + if (handleUV.IsAllocated) handleUV.Free(); + } + if (err != 0) + { + //EditorUtility.DisplayDialog("Bakery", "xatlas cancel", "OK"); + xatlasClear(atlas); + return; + } + + Debug.Log("xatlas time: " + (GetTime() - t)); + t = GetTime(); + + //EditorUtility.DisplayDialog("Bakery", "xatlas unwrap start", "OK"); + //var uv2 = new Vector2[m.vertexCount]; + //int vertexOffset = m.vertexCount; + //var newUV2 = new List<Vector2>(); + //var newXref = new List<int>(); + var indexBuffers = new List<int[]>(); + + newUVBuffer = new List<Vector2>(); + newXrefBuffer = new List<int>(); + 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; i<m.subMeshCount; i++) + { + // Get data from xatlas + int newVertCount = xatlasGetVertexCount(atlas, i); + int indexCount = xatlasGetIndexCount(atlas, i); // should be unchanged + + var uvBuffer = new Vector2[newVertCount]; + var xrefBuffer = new int[newVertCount]; + var indexBuffer = new int[indexCount]; + + 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(); + + xatlasGetData(atlas, i, pointerT, pointerX, pointerI); + } + finally + { + if (handleT.IsAllocated) handleT.Free(); + if (handleX.IsAllocated) handleX.Free(); + if (handleI.IsAllocated) handleI.Free(); + } + + // Generate new UV buffer and xatlas->final index mappings + var xatlasIndexToNewIndex = new int[newVertCount]; + for(int j=0; j<newVertCount; j++) + { + int xref = xrefBuffer[j]; + Vector2 uv = uvBuffer[j]; + + if (newUVBuffer[xref].x < 0) + { + // first xref encounter gets UV + xatlasIndexToNewIndex[j] = xref; + newUVBuffer[xref] = uv; + newXrefBuffer[xref] = xref; + } + else if (newUVBuffer[xref].x == uv.x && newUVBuffer[xref].y == uv.y) + { + // vertex already added + xatlasIndexToNewIndex[j] = xref; + } + else + { + // duplicate vertex + xatlasIndexToNewIndex[j] = newUVBuffer.Count; + newUVBuffer.Add(uv); + newXrefBuffer.Add(xref); + } + } + + // Generate final index buffer + for(int j=0; j<indexCount; j++) + { + indexBuffer[j] = xatlasIndexToNewIndex[ indexBuffer[j] ]; + } + indexBuffers.Add(indexBuffer); + } + + //EditorUtility.DisplayDialog("Bakery", "xatlas unwrap end", "OK"); + + int vertCount = m.vertexCount; + + bool origIs16bit = true; +#if UNITY_2017_3_OR_NEWER + origIs16bit = m.indexFormat == UnityEngine.Rendering.IndexFormat.UInt16; +#endif + bool is32bit = newUVBuffer.Count >= 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<Vector3>(newXrefBuffer, positions); + m.normals = FillAtrribute<Vector3>(newXrefBuffer, normals); + m.boneWeights = FillAtrribute<BoneWeight>(newXrefBuffer, m.boneWeights); + m.colors32 = FillAtrribute<Color32>(newXrefBuffer, m.colors32); + m.tangents = FillAtrribute<Vector4>(newXrefBuffer, m.tangents); + m.uv = FillAtrribute<Vector2>(newXrefBuffer, m.uv); + m.uv3 = FillAtrribute<Vector2>(newXrefBuffer, m.uv3); + m.uv4 = FillAtrribute<Vector2>(newXrefBuffer, m.uv4); +#if UNITY_2018_2_OR_NEWER + m.uv5 = FillAtrribute<Vector2>(newXrefBuffer, m.uv5); + m.uv6 = FillAtrribute<Vector2>(newXrefBuffer, m.uv6); + m.uv7 = FillAtrribute<Vector2>(newXrefBuffer, m.uv7); + m.uv8 = FillAtrribute<Vector2>(newXrefBuffer, m.uv8); +#endif + } + + m.uv2 = newUVBuffer.ToArray(); + +/* + + // Set new UV2 + var finalUV2 = new Vector2[vertCount + newUV2.Count]; + for(int i=0; i<vertCount; i++) finalUV2[i] = uv2[i]; + for(int i=0; i<newUV2.Count; i++) finalUV2[i + vertCount] = newUV2[i]; + m.uv2 = finalUV2; +*/ + // Set indices + for(int i=0; i<m.subMeshCount; i++) + { + m.SetTriangles(indexBuffers[i], i); + } + + //Debug.Log("post-xatlas mesh building time: " + GetTime() - t)); + + xatlasClear(atlas); + } +} diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas.cs.meta new file mode 100644 index 00000000..ba1cb5bf --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlas.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 144aa3fbdb8360b4aaa4051032c25680 +timeCreated: 1557759843 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasEnable.cs b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasEnable.cs new file mode 100644 index 00000000..40b47ade --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasEnable.cs @@ -0,0 +1,13 @@ +using UnityEngine; +using UnityEditor; +using UnityEditor.SceneManagement; +using System.Collections.Generic; + +public partial class ftModelPostProcessor : ftModelPostProcessorInternal +{ + public override void UnwrapXatlas(Mesh m, UnwrapParam param) + { + xatlas.Unwrap(m, uparams); + } +} + diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasEnable.cs.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasEnable.cs.meta new file mode 100644 index 00000000..2a87e7fc --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasEnable.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: deadc446e4efea944b443c59b6aed3f8 +timeCreated: 1559601034 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasLib.dll b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasLib.dll Binary files differnew file mode 100644 index 00000000..d0491338 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasLib.dll diff --git a/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasLib.dll.meta b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasLib.dll.meta new file mode 100644 index 00000000..4ebc077a --- /dev/null +++ b/VRCSDK3Worlds/Assets/Editor/x64/Bakery/scripts/xatlas/xatlasLib.dll.meta @@ -0,0 +1,28 @@ +fileFormatVersion: 2 +guid: b8be677e296fbf94092b02ac72dab402 +timeCreated: 1582151152 +licenseType: Store +PluginImporter: + serializedVersion: 2 + iconMap: {} + executionOrder: {} + isPreloaded: 0 + isOverridable: 0 + platformData: + data: + first: + Any: + second: + enabled: 0 + settings: {} + data: + first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: |