diff --git a/Editor/Mono/Annotation/AnnotationWindow.cs b/Editor/Mono/Annotation/AnnotationWindow.cs index 813a5c3fb7..ed885a0b41 100644 --- a/Editor/Mono/Annotation/AnnotationWindow.cs +++ b/Editor/Mono/Annotation/AnnotationWindow.cs @@ -173,6 +173,15 @@ internal static bool ShowAtPosition(Rect buttonRect, bool isGameView) Event.current.Use(); if (s_AnnotationWindow == null) s_AnnotationWindow = ScriptableObject.CreateInstance(); + else + { + // We are treating AnnotationWindow like a PopupWindow which has logic to reclose it when opened, + // AuxWindows derived from EditorWindow reset/reopen when repeatedly clicking the open button by design. + // Call Cancel() here if it is already open. + s_AnnotationWindow.Cancel(); + return false; + } + s_AnnotationWindow.Init(buttonRect, isGameView); return true; } diff --git a/Editor/Mono/AssetPipeline/TextureImporterTypes.bindings.cs b/Editor/Mono/AssetPipeline/TextureImporterTypes.bindings.cs index 12ee970b3d..368a5dcf3e 100644 --- a/Editor/Mono/AssetPipeline/TextureImporterTypes.bindings.cs +++ b/Editor/Mono/AssetPipeline/TextureImporterTypes.bindings.cs @@ -171,6 +171,10 @@ public sealed partial class TextureImporterSettings [SerializeField] private int m_TextureFormatSet; + //For backward compatibility for an incorrectly applied gamma decoding step (bug) + [SerializeField] + int m_ApplyGammaDecoding; + public TextureImporterType textureType { get {return (TextureImporterType)m_TextureType; } diff --git a/Editor/Mono/AssetStore/AssetStoreAsset.cs b/Editor/Mono/AssetStore/AssetStoreAsset.cs index 52ebb6f720..137b5b3546 100644 --- a/Editor/Mono/AssetStore/AssetStoreAsset.cs +++ b/Editor/Mono/AssetStore/AssetStoreAsset.cs @@ -143,4 +143,678 @@ internal string DebugString } } } -} // UnityEditor namespace + + /** + * An asset store asset selection. + * + * This class works as a singleton for keeping the list of selected + * asset store assets. This is not handled by the normal select framework + * because an asset store asset does not have an instanceID since it is not + * actually an asset in the local project. + * + * Currently there is only support for handling a single selected asset store + * asset at a time. This class is somewhat prepared for multiple asset store asset + * selections though. + */ + internal static class AssetStoreAssetSelection + { + public delegate void AssetsRefreshed(); + + static internal Dictionary s_SelectedAssets; + + public static void AddAsset(AssetStoreAsset searchResult, Texture2D placeholderPreviewImage) + { + if (placeholderPreviewImage != null) + searchResult.previewImage = ScaleImage(placeholderPreviewImage, 256, 256); + + searchResult.previewInfo = null; + searchResult.previewBundleRequest = null; + + // Dynamic previews is asset bundles to be displayed in + // the inspector. Static previews are images. + if (!string.IsNullOrEmpty(searchResult.dynamicPreviewURL) && searchResult.previewBundle == null) + { + // Debug.Log("dyn url " + searchResult.disposed.ToString() + " " + searchResult.dynamicPreviewURL); + searchResult.disposed = false; + // searchResult.previewBundle = AssetBundle.CreateFromFile("/users/jonasd/test.unity3d"); + // searchResult.previewAsset = searchResult.previewBundle.mainAsset; + + // Request the asset bundle data from the url and register a callback + AsyncHTTPClient client = new AsyncHTTPClient(searchResult.dynamicPreviewURL); + client.doneCallback = delegate(IAsyncHTTPClient c) { + if (!client.IsSuccess()) + { + System.Console.WriteLine("Error downloading dynamic preview: " + client.text); + // Try the static preview instead + searchResult.dynamicPreviewURL = null; + DownloadStaticPreview(searchResult); + return; + } + + // We only suppport one asset so grab the first one + AssetStoreAsset sel = GetFirstAsset(); + + // Make sure that the selection hasn't changed meanwhile + if (searchResult.disposed || sel == null || searchResult.id != sel.id) + { + //Debug.Log("dyn disposed " + searchResult.disposed.ToString() + " " + (sel == null ? "null" : sel.id.ToString()) + " " + searchResult.id.ToString()); + return; + } + + // Go create the asset bundle in memory from the binary blob asynchronously + try + { + AssetBundleCreateRequest cr = AssetBundle.LoadFromMemoryAsync(c.bytes); + + // Workaround: Don't subject the bundle to the usual compatibility checks. We want + // to stay compatible with previews created in prior versions of Unity and with the + // stuff we put into previews, we should generally be able to still load the content + // in the editor. + cr.DisableCompatibilityChecks(); + + searchResult.previewBundleRequest = cr; + EditorApplication.CallbackFunction callback = null; + + // The callback will be called each tick and check if the asset bundle is ready + double startTime = EditorApplication.timeSinceStartup; + callback = () => { + AssetStoreUtils.UpdatePreloading(); + + if (!cr.isDone) + { + double nowTime = EditorApplication.timeSinceStartup; + if (nowTime - startTime > 10.0) + { + // Timeout. Stop polling + EditorApplication.update -= callback; + System.Console.WriteLine("Timed out fetch live preview bundle " + + (searchResult.dynamicPreviewURL ?? "")); + // Debug.Log("Not done Timed out" + cr.progress.ToString() ); + } + else + { + // Debug.Log("Not done " + cr.progress.ToString() ); + } + return; + } + + // Done cooking. Stop polling. + EditorApplication.update -= callback; + + // Make sure that the selection hasn't changed meanwhile + AssetStoreAsset sel2 = GetFirstAsset(); + if (searchResult.disposed || sel2 == null || searchResult.id != sel2.id) + { + // No problem. Just ignore. + // Debug.Log("dyn late disposed " + searchResult.disposed.ToString() + " " + (sel2 == null ? "null" : sel2.id.ToString()) + " " + searchResult.id.ToString()); + } + else + { + searchResult.previewBundle = cr.assetBundle; +#pragma warning disable 618 + if (cr.assetBundle == null || cr.assetBundle.mainAsset == null) + { + // Failed downloading live preview. Fallback to static + searchResult.dynamicPreviewURL = null; + DownloadStaticPreview(searchResult); + } + else + searchResult.previewAsset = searchResult.previewBundle.mainAsset; +#pragma warning restore 618 + } + }; + + EditorApplication.update += callback; + } + catch (System.Exception e) + { + System.Console.Write(e.Message); + Debug.Log(e.Message); + } + }; + client.Begin(); + } + else if (!string.IsNullOrEmpty(searchResult.staticPreviewURL)) + { + DownloadStaticPreview(searchResult); + } + + // searchResult.previewBundle = null; + AddAssetInternal(searchResult); + + RefreshFromServer(null); + } + + // Also used by AssetStoreToolUtils + internal static void AddAssetInternal(AssetStoreAsset searchResult) + { + if (s_SelectedAssets == null) + s_SelectedAssets = new Dictionary(); + s_SelectedAssets[searchResult.id] = searchResult; + } + + static void DownloadStaticPreview(AssetStoreAsset searchResult) + { + AsyncHTTPClient client = new AsyncHTTPClient(searchResult.staticPreviewURL); + client.doneCallback = delegate(IAsyncHTTPClient c) { + if (!client.IsSuccess()) + { + System.Console.WriteLine("Error downloading static preview: " + client.text); + // Debug.LogError("Error downloading static preview: " + client.text); + return; + } + + // Need to put the texture through some scaling magic in order for the + // TextureInspector to be able to show it. + // TODO: This is a workaround and should be fixed. + Texture2D srcTex = c.texture; + Texture2D tex = new Texture2D(srcTex.width, srcTex.height, TextureFormat.RGB24, false, true); + AssetStorePreviewManager.ScaleImage(tex.width, tex.height, srcTex, tex, null); + // tex.Compress(true); + searchResult.previewImage = tex; + + Object.DestroyImmediate(srcTex); + AssetStoreAssetInspector.Instance.Repaint(); + }; + client.Begin(); + } + + // Refresh information about displayed asset by quering the + // asset store server. This is typically after the user has + // logged in because we need to know if he already owns the + // displayed asset. + public static void RefreshFromServer(AssetsRefreshed callback) + { + if (s_SelectedAssets.Count == 0) + return; + + // Refetch assetInfo + // Query the asset store for more info + List queryAssets = new List(); + foreach (KeyValuePair qasset in s_SelectedAssets) + queryAssets.Add(qasset.Value); + + // This will fill the queryAssets with extra preview data + AssetStoreClient.AssetsInfo(queryAssets, + delegate(AssetStoreAssetsInfo results) { + AssetStoreAssetInspector.paymentAvailability = AssetStoreAssetInspector.PaymentAvailability.ServiceDisabled; + if (!string.IsNullOrEmpty(results.error)) + { + System.Console.WriteLine("Error performing Asset Store Info search: " + results.error); + AssetStoreAssetInspector.OfflineNoticeEnabled = true; + //Debug.LogError("Error performing Asset Store Info search: " + results.error); + if (callback != null) callback(); + return; + } + AssetStoreAssetInspector.OfflineNoticeEnabled = false; + + if (results.status == AssetStoreAssetsInfo.Status.Ok) + AssetStoreAssetInspector.paymentAvailability = AssetStoreAssetInspector.PaymentAvailability.Ok; + else if (results.status == AssetStoreAssetsInfo.Status.BasketNotEmpty) + AssetStoreAssetInspector.paymentAvailability = AssetStoreAssetInspector.PaymentAvailability.BasketNotEmpty; + else if (results.status == AssetStoreAssetsInfo.Status.AnonymousUser) + AssetStoreAssetInspector.paymentAvailability = AssetStoreAssetInspector.PaymentAvailability.AnonymousUser; + + AssetStoreAssetInspector.s_PurchaseMessage = results.message; + AssetStoreAssetInspector.s_PaymentMethodCard = results.paymentMethodCard; + AssetStoreAssetInspector.s_PaymentMethodExpire = results.paymentMethodExpire; + AssetStoreAssetInspector.s_PriceText = results.priceText; + + AssetStoreAssetInspector.Instance.Repaint(); + if (callback != null) callback(); + }); + } + + private static Texture2D ScaleImage(Texture2D source, int w, int h) + { + // Bug: When scaling down things look weird unless the source size is + // == 0 when mod 4. Therefore we just return null if that's the case. + if (source.width % 4 != 0) + return null; + + Texture2D result = new Texture2D(w, h, TextureFormat.RGB24, false, true); + Color[] rpixels = result.GetPixels(0); + + double dx = 1.0 / (double)w; + double dy = 1.0 / (double)h; + double x = 0; + double y = 0; + int idx = 0; + for (int j = 0; j < h; j++) + { + for (int i = 0; i < w; i++, idx++) + { + rpixels[idx] = source.GetPixelBilinear((float)x, (float)y); + x += dx; + } + x = 0; + y += dy; + } + result.SetPixels(rpixels, 0); + result.Apply(); + return result; + } + + public static bool ContainsAsset(int id) + { + return s_SelectedAssets != null && s_SelectedAssets.ContainsKey(id); + } + + public static void Clear() + { + if (s_SelectedAssets == null) + return; + foreach (var kv in s_SelectedAssets) + kv.Value.Dispose(); + + s_SelectedAssets.Clear(); + } + + public static int Count + { + get { return s_SelectedAssets == null ? 0 : s_SelectedAssets.Count; } + } + + public static bool Empty + { + get { return s_SelectedAssets == null ? true : s_SelectedAssets.Count == 0; } + } + + public static AssetStoreAsset GetFirstAsset() + { + if (s_SelectedAssets == null) + return null; + var i = s_SelectedAssets.GetEnumerator(); + if (!i.MoveNext()) + return null; + return i.Current.Value; + } + } + + /** + * In addition to being an inspector for AssetStoreAssets this + * inspector works as the object that is selected when an + * asset store asset is selected ie. + * Selection.object == AssetStoreAssetInspector.Instance + * when asset store assets are selected. + */ + [CustomEditor(typeof(AssetStoreAssetInspector))] + internal class AssetStoreAssetInspector : Editor + { + static AssetStoreAssetInspector s_SharedAssetStoreAssetInspector; + + public static AssetStoreAssetInspector Instance + { + get + { + if (s_SharedAssetStoreAssetInspector == null) + { + s_SharedAssetStoreAssetInspector = ScriptableObject.CreateInstance(); + s_SharedAssetStoreAssetInspector.hideFlags = HideFlags.HideAndDontSave; + } + return s_SharedAssetStoreAssetInspector; + } + } + + class Styles + { + public GUIStyle link = new GUIStyle(EditorStyles.label); + public Styles() + { + link.normal.textColor = new Color(.26f, .51f, .75f, 1f); + } + } + + static Styles styles; + + bool packageInfoShown = true; + + // Payment info for all selected assets + internal static string s_PurchaseMessage = ""; + internal static string s_PaymentMethodCard = ""; + internal static string s_PaymentMethodExpire = ""; + internal static string s_PriceText = ""; + static GUIContent[] sStatusWheel; + + public static bool OfflineNoticeEnabled { get; set; } + + // Asset store payment availability + internal enum PaymentAvailability + { + BasketNotEmpty, + ServiceDisabled, + AnonymousUser, + Ok + } + + internal static PaymentAvailability m_PaymentAvailability; + internal static PaymentAvailability paymentAvailability + { + get + { + if (AssetStoreClient.LoggedOut()) + m_PaymentAvailability = PaymentAvailability.AnonymousUser; + return m_PaymentAvailability; + } + set + { + if (AssetStoreClient.LoggedOut()) + m_PaymentAvailability = PaymentAvailability.AnonymousUser; + else + m_PaymentAvailability = value; + } + } + + int lastAssetID; + + // Callback for curl to call + public void OnDownloadProgress(string id, string message, int bytes, int total) + { + AssetStoreAsset activeAsset = AssetStoreAssetSelection.GetFirstAsset(); + if (activeAsset == null) return; + AssetStoreAsset.PreviewInfo info = activeAsset.previewInfo; + if (info == null) return; + + if (activeAsset.packageID.ToString() != id) + return; + + if ((message == "downloading" || message == "connecting") && !OfflineNoticeEnabled) + { + info.downloadProgress = (float)bytes / (float)total; + } + else + { + info.downloadProgress = -1f; + } + Repaint(); + } + + public void Update() + { + // Repaint if asset has changed. + // This has to be done here because the .target is always set to + // this inspector when inspecting asset store assets. + AssetStoreAsset a = AssetStoreAssetSelection.GetFirstAsset(); + bool hasProgress = a != null && a.previewInfo != null && (a.previewInfo.buildProgress >= 0f || a.previewInfo.downloadProgress >= 0f); + if ((a == null && lastAssetID != 0) || + (a != null && lastAssetID != a.id) || + hasProgress) + { + lastAssetID = a == null ? 0 : a.id; + Repaint(); + } + + // Repaint when the main asset of a possibly downloaded bundle is ready for preview + if (a != null && a.previewBundle != null) + { + a.previewBundle.Unload(false); + a.previewBundle = null; + Repaint(); + } + } + + public override void OnInspectorGUI() + { + if (styles == null) + { + // Set the singleton in case the DrawEditors() has created this window + s_SharedAssetStoreAssetInspector = this; + styles = new Styles(); + } + + AssetStoreAsset activeAsset = AssetStoreAssetSelection.GetFirstAsset(); + AssetStoreAsset.PreviewInfo info = null; + if (activeAsset != null) + info = activeAsset.previewInfo; + + if (activeAsset != null) + target.name = string.Format("Asset Store: {0}", activeAsset.name); + else + target.name = "Asset Store"; + + EditorGUILayout.BeginVertical(); + + bool guiEnabled = GUI.enabled; + + GUI.enabled = activeAsset != null && activeAsset.packageID != 0; + + if (OfflineNoticeEnabled) + { + Color col = GUI.color; + GUI.color = Color.yellow; + GUILayout.Label("Network is offline"); + GUI.color = col; + } + + if (activeAsset != null) + { + string typeName = activeAsset.className == null ? "" : activeAsset.className.Split(new char[] {' '}, 2)[0]; + bool isPackage = activeAsset.id == -activeAsset.packageID; + if (isPackage) + typeName = "Package"; + if (activeAsset.HasLivePreview) + typeName = activeAsset.Preview.GetType().Name; + EditorGUILayout.LabelField("Type", typeName); + + if (isPackage) + { + packageInfoShown = true; + } + else + { + EditorGUILayout.Separator(); + packageInfoShown = EditorGUILayout.Foldout(packageInfoShown , "Part of package", true); + } + if (packageInfoShown) + { + EditorGUILayout.LabelField("Name", info == null ? "-" : info.packageName); + EditorGUILayout.LabelField("Version", info == null ? "-" : info.packageVersion); + string price = info == null ? "-" : (!string.IsNullOrEmpty(activeAsset.price) ? activeAsset.price : "free"); + EditorGUILayout.LabelField("Price", price); + string rating = info != null && info.packageRating >= 0 ? info.packageRating + " of 5" : "-"; + EditorGUILayout.LabelField("Rating", rating); + EditorGUILayout.LabelField("Size", info == null ? "-" : intToSizeString(info.packageSize)); + string assetCount = info != null && info.packageAssetCount >= 0 ? info.packageAssetCount.ToString() : "-"; + EditorGUILayout.LabelField("Asset count", assetCount); + GUILayout.BeginHorizontal(); + EditorGUILayout.PrefixLabel("Web page"); + bool hasPageUrl = info != null && info.packageShortUrl != null && info.packageShortUrl != ""; + bool guiBefore = GUI.enabled; + GUI.enabled = hasPageUrl; + + if (GUILayout.Button(hasPageUrl ? new GUIContent(info.packageShortUrl, "View in browser") : EditorGUIUtility.TempContent("-"), styles.link)) + { + Application.OpenURL(info.packageShortUrl); + } + if (GUI.enabled) + EditorGUIUtility.AddCursorRect(GUILayoutUtility.GetLastRect(), MouseCursor.Link); + GUI.enabled = guiBefore; + GUILayout.EndHorizontal(); + EditorGUILayout.LabelField("Publisher", info == null ? "-" : info.publisherName); + } + + if (activeAsset.id != 0) + { + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + + if (GUILayout.Button("Open Asset Store", GUILayout.Height(40), GUILayout.Width(120))) + { + OpenItemInAssetStore(activeAsset); + GUIUtility.ExitGUI(); + } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + } + GUILayout.FlexibleSpace(); + } + EditorWrapper editor = previewEditor; + if (editor != null && activeAsset != null && activeAsset.HasLivePreview) + editor.OnAssetStoreInspectorGUI(); + + GUI.enabled = guiEnabled; + + EditorGUILayout.EndVertical(); + } + + public static void OpenItemInAssetStore(AssetStoreAsset activeAsset) + { + if (activeAsset.id != 0) + { + AssetStore.Open(string.Format("content/{0}?assetID={1}", activeAsset.packageID, activeAsset.id)); + } + } + + private static string intToSizeString(int inValue) + { + if (inValue < 0) + return "unknown"; + float val = (float)inValue; + string[] scale = new string[] { "TB", "GB", "MB", "KB", "Bytes" }; + int idx = scale.Length - 1; + while (val > 1000.0f && idx >= 0) + { + val /= 1000f; + idx--; + } + + if (idx < 0) + return ""; + + return UnityString.Format("{0:#.##} {1}", val, scale[idx]); + } + + public override bool HasPreviewGUI() + { + return (target != null && AssetStoreAssetSelection.Count != 0); + } + + EditorWrapper m_PreviewEditor; + Object m_PreviewObject; + + public void OnEnable() + { + EditorApplication.update += Update; + AssetStoreUtils.RegisterDownloadDelegate(this); + } + + public void OnDisable() + { + EditorApplication.update -= Update; + if (m_PreviewEditor != null) + { + m_PreviewEditor.Dispose(); + m_PreviewEditor = null; + } + if (m_PreviewObject != null) + m_PreviewObject = null; + AssetStoreUtils.UnRegisterDownloadDelegate(this); + } + + private EditorWrapper previewEditor + { + get + { + AssetStoreAsset asset = AssetStoreAssetSelection.GetFirstAsset(); + if (asset == null) return null; + Object preview = asset.Preview; + if (preview == null) return null; + + if (preview != m_PreviewObject) + { + m_PreviewObject = preview; + + if (m_PreviewEditor != null) + m_PreviewEditor.Dispose(); + + m_PreviewEditor = EditorWrapper.Make(m_PreviewObject, EditorFeatures.PreviewGUI); + } + + return m_PreviewEditor; + } + } + + public override void OnPreviewSettings() + { + AssetStoreAsset asset = AssetStoreAssetSelection.GetFirstAsset(); + if (asset == null) return; + + EditorWrapper editor = previewEditor; + if (editor != null && asset.HasLivePreview) + editor.OnPreviewSettings(); + } + + public override string GetInfoString() + { + EditorWrapper editor = previewEditor; + AssetStoreAsset a = AssetStoreAssetSelection.GetFirstAsset(); + if (a == null) + return "No item selected"; + + if (editor != null && a.HasLivePreview) + return editor.GetInfoString(); + + return ""; + } + + public override void OnPreviewGUI(Rect r, GUIStyle background) + { + if (m_PreviewObject == null) return; + EditorWrapper editor = previewEditor; + + // Special handling for animation clips because they only have + // an interactive preview available which shows play button etc. + // The OnPreviewGUI is also used for the small icons in the top + // of the inspectors where buttons should not be rendered. + + if (editor != null && m_PreviewObject is AnimationClip) + editor.OnPreviewGUI(r, background); // currently renders nothing for animation clips + else + OnInteractivePreviewGUI(r, background); + } + + public override void OnInteractivePreviewGUI(Rect r, GUIStyle background) + { + EditorWrapper editor = previewEditor; + if (editor != null) + { + editor.OnInteractivePreviewGUI(r, background); + } + + // If the live preview is not available yes the show a spinner + AssetStoreAsset a = AssetStoreAssetSelection.GetFirstAsset(); + if (a != null && !a.HasLivePreview && !string.IsNullOrEmpty(a.dynamicPreviewURL)) + { + GUIContent c = StatusWheel; + r.y += (r.height - c.image.height) / 2f; + r.x += (r.width - c.image.width) / 2f; + GUI.Label(r, StatusWheel); + Repaint(); + } + } + + static GUIContent StatusWheel + { + get + { + if (sStatusWheel == null) + { + sStatusWheel = new GUIContent[12]; + for (int i = 0; i < 12; i++) + { + GUIContent gc = new GUIContent(); + gc.image = EditorGUIUtility.LoadIcon("WaitSpin" + i.ToString("00")) as Texture2D; + sStatusWheel[i] = gc; + } + } + int frame = (int)Mathf.Repeat(Time.realtimeSinceStartup * 10, 11.99f); + return sStatusWheel[frame]; + } + } + + public override GUIContent GetPreviewTitle() + { + return GUIContent.Temp("Asset Store Preview"); + } + } // Inspector class +} diff --git a/Editor/Mono/Collab/Collab.bindings.cs b/Editor/Mono/Collab/Collab.bindings.cs index 1b6edb9c3a..708da8d0c2 100644 --- a/Editor/Mono/Collab/Collab.bindings.cs +++ b/Editor/Mono/Collab/Collab.bindings.cs @@ -71,6 +71,8 @@ public static extern int GetRevisionsData( public extern void SetSeat(bool value); + public extern void RefreshSeatAvailabilityAsync(); + public extern string GetProjectGUID(); public extern bool ShouldDoInitialCommit(); diff --git a/Editor/Mono/ConsoleWindow.cs b/Editor/Mono/ConsoleWindow.cs index 8c2b41192d..53aeb818ab 100644 --- a/Editor/Mono/ConsoleWindow.cs +++ b/Editor/Mono/ConsoleWindow.cs @@ -147,6 +147,9 @@ static class Content public ConsoleAttachToPlayerState(EditorWindow parentWindow, Action connectedCallback = null) : base(parentWindow, connectedCallback) { + // This is needed to force initialize the instance and the state so that messages from players are received and printed to the console (if that is the serialized state) + // on creation of the ConsoleWindow UI instead of when the uer first clicks on the dropdown, and triggers AddItemsToMenu. + PlayerConnectionLogReceiver.instance.State = PlayerConnectionLogReceiver.instance.State; } bool IsConnected() @@ -303,6 +306,7 @@ internal void OnEnable() // Update the filter on enable for DomainReload(keep current filter) and window opening(reset filter because m_searchText is null) SetFilter(LogEntries.GetFilteringText()); + wantsLessLayoutEvents = true; titleContent = GetLocalizedTitleContent(); ms_ConsoleWindow = this; m_DevBuild = Unsupported.IsDeveloperMode(); diff --git a/Editor/Mono/EditorGUI.cs b/Editor/Mono/EditorGUI.cs index 650c338e73..1971ee3c24 100644 --- a/Editor/Mono/EditorGUI.cs +++ b/Editor/Mono/EditorGUI.cs @@ -7835,14 +7835,16 @@ internal static GUIContent[] GetEnumTypeLocalizedGUIContents(Type enumType, Enum internal static GUIContent[] GetEnumLocalizedGUIContents(SerializedProperty property) { var propertyHash = property.hashCodeForPropertyPathWithoutArrayIndex; + var typeHash = property.serializedObject.targetObject.GetType().GetHashCode(); + var hashCode = typeHash ^ propertyHash; GUIContent[] result; - if (s_SerializedPropertyEnumLocalizedGUIContents.TryGetValue(propertyHash, out result)) + if (s_SerializedPropertyEnumLocalizedGUIContents.TryGetValue(hashCode, out result)) { return result; } result = EditorGUIUtility.TempContent(property.enumLocalizedDisplayNames); - s_SerializedPropertyEnumLocalizedGUIContents[propertyHash] = result; + s_SerializedPropertyEnumLocalizedGUIContents[hashCode] = result; return result; } diff --git a/Editor/Mono/EditorHandles/TransformHandle.cs b/Editor/Mono/EditorHandles/TransformHandle.cs index 2545e56de6..51ca12cdf5 100644 --- a/Editor/Mono/EditorHandles/TransformHandle.cs +++ b/Editor/Mono/EditorHandles/TransformHandle.cs @@ -539,7 +539,6 @@ internal static void TransformHandle(TransformHandleIds ids, ref Vector3 positio { if (ids.position.Has(GUIUtility.hotControl)) { - workingRotation = TransformManipulator.mouseDownHandleRotation; position = DoPositionHandle_Internal(ids.position, position, workingRotation, pParam); } else if (ids.rotation.Has(GUIUtility.hotControl)) diff --git a/Editor/Mono/EditorResources.cs b/Editor/Mono/EditorResources.cs index c0372dcdca..8c02200800 100644 --- a/Editor/Mono/EditorResources.cs +++ b/Editor/Mono/EditorResources.cs @@ -105,7 +105,7 @@ public static FontDef CreateSystemFont(string fontName) fontDef.SetFont(Style.Small, "Fonts/System/System Small.ttf", new string[] {fontName}); fontDef.SetFont(Style.Normal, "Fonts/System/System Normal.ttf", new string[] { fontName }); - fontDef.SetFont(Style.Bold, "Fonts/System/System Normal Bold.ttf", new string[] { fontName + "Bold"}); + fontDef.SetFont(Style.Bold, "Fonts/System/System Normal Bold.ttf", new string[] { fontName + " Bold"}); return fontDef; } } diff --git a/Editor/Mono/EditorSettings.bindings.cs b/Editor/Mono/EditorSettings.bindings.cs index bbbddc8c2e..3b6dd2f21b 100644 --- a/Editor/Mono/EditorSettings.bindings.cs +++ b/Editor/Mono/EditorSettings.bindings.cs @@ -4,7 +4,6 @@ using System; using System.Linq; -using System.Runtime.InteropServices; using UnityEditor.VisualStudioIntegration; using UnityEngine.Bindings; using Object = UnityEngine.Object; diff --git a/Editor/Mono/EditorUtility.bindings.cs b/Editor/Mono/EditorUtility.bindings.cs index 850c880b90..cacb3f5eff 100644 --- a/Editor/Mono/EditorUtility.bindings.cs +++ b/Editor/Mono/EditorUtility.bindings.cs @@ -143,6 +143,9 @@ public static void UnloadUnusedAssetsIgnoreManagedReferences() [FreeFunction("ClearProgressbar")] public static extern void ClearProgressBar(); + [FreeFunction("BusyProgressDialogDelayChanged")] + internal static extern void BusyProgressDialogDelayChanged(float delay); + [FreeFunction("GetObjectEnabled")] public static extern int GetObjectEnabled(Object target); diff --git a/Editor/Mono/EditorWindow.cs b/Editor/Mono/EditorWindow.cs index 5db7c34302..98e12d072f 100644 --- a/Editor/Mono/EditorWindow.cs +++ b/Editor/Mono/EditorWindow.cs @@ -170,6 +170,20 @@ public bool wantsMouseEnterLeaveWindow } } + // Indicates that the editor window will only receive a layout pass before a repaint event. + public bool wantsLessLayoutEvents + { + get + { + return m_EventInterests.wantsLessLayoutEvents; + } + set + { + m_EventInterests.wantsLessLayoutEvents = value; + MakeParentsSettingsMatchMe(); + } + } + internal void CheckForWindowRepaint() { double time = EditorApplication.timeSinceStartup; diff --git a/Editor/Mono/GI/Lightmapping.bindings.cs b/Editor/Mono/GI/Lightmapping.bindings.cs index 9eeba859da..f366bb1fea 100644 --- a/Editor/Mono/GI/Lightmapping.bindings.cs +++ b/Editor/Mono/GI/Lightmapping.bindings.cs @@ -134,8 +134,8 @@ public enum GIWorkflowMode // [Obsolete("Lightmapping.giWorkflowMode is obsolete, use Lightmapping.lightingSettings.autoGenerate instead. ", false)] public static GIWorkflowMode giWorkflowMode { - get { return GetLightingSettingsOrDefaultsFallback().giWorkflowMode; } - set { GetOrCreateLightingsSettings().giWorkflowMode = value; } + get { return (GIWorkflowMode)GetLightingSettingsOrDefaultsFallback().giWorkflowMode; } + set { GetOrCreateLightingsSettings().giWorkflowMode = (LightingSettings.GIWorkflowMode)value; } } // [Obsolete("Lightmapping.realtimeGI is obsolete, use Lightmapping.lightingSettings.realtimeGI instead. ", false)] diff --git a/Editor/Mono/GUI/AppStatusBar.cs b/Editor/Mono/GUI/AppStatusBar.cs index 5e596cef4b..a93802640b 100644 --- a/Editor/Mono/GUI/AppStatusBar.cs +++ b/Editor/Mono/GUI/AppStatusBar.cs @@ -14,7 +14,7 @@ internal class AppStatusBar : GUIView { static readonly NumberFormatInfo percentageFormat = new CultureInfo("en-US", false).NumberFormat; - static class Styles + internal static class Styles { public const int spacing = 4; @@ -33,9 +33,10 @@ static class Styles public static readonly GUIContent[] statusWheel; public static readonly GUIContent assemblyLock = EditorGUIUtility.IconContent("AssemblyLock", "|Assemblies are currently locked. Compilation will resume once they are unlocked"); - public static readonly GUIContent progressIcon = EditorGUIUtility.IconContent("Progress", "Open progress details..."); - public static readonly GUIContent autoGenerateLightOn = EditorGUIUtility.IconContent("AutoLightbakingOn", "Auto Generate Lighting On"); - public static readonly GUIContent autoGenerateLightOff = EditorGUIUtility.IconContent("AutoLightbakingOff", "Auto Generate Lighting Off"); + public static readonly GUIContent progressIcon = EditorGUIUtility.TrIconContent("Progress", "Show progress details"); + public static readonly GUIContent progressHideIcon = EditorGUIUtility.TrIconContent("Progress", "Hide progress details"); + public static readonly GUIContent autoGenerateLightOn = EditorGUIUtility.TrIconContent("AutoLightbakingOn", "Auto Generate Lighting On"); + public static readonly GUIContent autoGenerateLightOff = EditorGUIUtility.TrIconContent("AutoLightbakingOff", "Auto Generate Lighting Off"); static Styles() { @@ -83,6 +84,7 @@ protected override void OnEnable() m_autoLightBakingOn = GetBakeMode(); m_ManagedDebuggerToggle = new ManagedDebuggerToggle(); m_CacheServerToggle = new CacheServerToggle(); + m_EventInterests.wantsLessLayoutEvents = true; Progress.added += RefreshProgressBar; Progress.removed += RefreshProgressBar; @@ -179,8 +181,14 @@ private void DrawRefreshStatus() } else { - if (GUILayout.Button(Styles.progressIcon, Styles.statusIcon)) - Progress.ShowDetails(); + var canHide = ProgressWindow.canHideDetails; + if (GUILayout.Button(canHide ? Styles.progressHideIcon : Styles.progressIcon, Styles.statusIcon)) + { + if (canHide) + ProgressWindow.HideDetails(); + else + Progress.ShowDetails(); + } var buttonRect = GUILayoutUtility.GetLastRect(); EditorGUIUtility.AddCursorRect(buttonRect, MouseCursor.Link); @@ -286,16 +294,14 @@ private void DrawBakeMode() EditorGUIUtility.AddCursorRect(buttonRect, MouseCursor.Link); } - private void DrawDebuggerToggle() + void DrawDebuggerToggle() { - var rect = EditorGUILayout.GetControlRect(GUILayout.Width(m_ManagedDebuggerToggle.GetWidth() - 3)); - m_ManagedDebuggerToggle.OnGUI(rect.x, 0); + m_ManagedDebuggerToggle.OnGUI(); } - private void DrawCacheServerToggle() + void DrawCacheServerToggle() { - var rect = EditorGUILayout.GetControlRect(GUILayout.Width(m_CacheServerToggle.GetWidth())); - m_CacheServerToggle.OnGUI(rect.x, 0); + m_CacheServerToggle.OnGUI(); } private void DrawStatusText() @@ -416,8 +422,7 @@ private GUILayoutOption GetStatusTextLayoutOption(GUIContent progressContent, fl { int iconWidth = 25; float specialModeLabelWidth = Styles.statusLabel.CalcSize(new GUIContent(m_SpecialModeLabel)).x + k_SpaceBeforeProgress + 8; - float debugToggleWidth = m_ManagedDebuggerToggle.GetWidth() + 1; - float statusRightReservedSpace = specialModeLabelWidth + debugToggleWidth + (showBakeMode ? (iconWidth) : 0) + iconWidth; + float statusRightReservedSpace = specialModeLabelWidth + iconWidth + (showBakeMode ? iconWidth : 0) + iconWidth; if (!showProgress) return GUILayout.MaxWidth(position.width - statusRightReservedSpace - consoleIconWidth); diff --git a/Editor/Mono/GUI/CacheServerToggle.cs b/Editor/Mono/GUI/CacheServerToggle.cs index df898257a7..2b8af67fc1 100644 --- a/Editor/Mono/GUI/CacheServerToggle.cs +++ b/Editor/Mono/GUI/CacheServerToggle.cs @@ -14,11 +14,6 @@ internal class CacheServerToggle private readonly GUIContent m_CacheServerConnectedContent; private readonly PopupLocation[] m_PopupLocation; - private const int k_Width = 36; - private const int k_Height = 19; - private const int k_MarginX = 4; - private const int k_MarginY = 0; - static CacheServerToggle() { AssetDatabaseExperimental.cacheServerConnectionChanged += OnCacherServerConnectionChanged; @@ -26,38 +21,22 @@ static CacheServerToggle() public CacheServerToggle() { - m_CacheServerNotEnabledContent = EditorGUIUtility.TrIconContent("CacheServerDisabled"); - m_CacheServerDisconnectedContent = EditorGUIUtility.TrIconContent("CacheServerDisconnected"); - m_CacheServerConnectedContent = EditorGUIUtility.TrIconContent("CacheServerConnected"); + m_CacheServerNotEnabledContent = EditorGUIUtility.TrIconContent("CacheServerDisabled", "Cache Server disabled"); + m_CacheServerDisconnectedContent = EditorGUIUtility.TrIconContent("CacheServerDisconnected", "Cache Server disconnected"); + m_CacheServerConnectedContent = EditorGUIUtility.TrIconContent("CacheServerConnected", "Cache Server connected"); m_PopupLocation = new[] { PopupLocation.AboveAlignRight }; } - public void OnGUI(float x, float y) + public void OnGUI() { - GUILayout.BeginVertical(); - EditorGUILayout.Space(); - - var statusContent = GetStatusContent(); - var buttonArea = new Rect(x + k_MarginX, y + k_MarginY, k_Width, k_Height); - - if (EditorGUI.DropdownButton(buttonArea, statusContent, FocusType.Passive, EditorStyles.toolbarDropDown)) + var content = GetStatusContent(); + var style = AppStatusBar.Styles.statusIcon; + var rect = GUILayoutUtility.GetRect(content, style); + if (GUI.Button(rect, content, style)) { - PopupWindow.Show(buttonArea, new CacheServerWindow(), m_PopupLocation); + PopupWindow.Show(rect, new CacheServerWindow(), m_PopupLocation); GUIUtility.ExitGUI(); } - - EditorGUILayout.Space(); - GUILayout.EndVertical(); - } - - public float GetWidth() - { - return k_Width + (k_MarginX << 1); - } - - public float GetHeight() - { - return k_Height + (k_MarginY << 1); } private GUIContent GetStatusContent() diff --git a/Editor/Mono/GUI/DockArea.cs b/Editor/Mono/GUI/DockArea.cs index 4acd47565c..e0869eebb9 100644 --- a/Editor/Mono/GUI/DockArea.cs +++ b/Editor/Mono/GUI/DockArea.cs @@ -229,10 +229,7 @@ public void RemoveTab(EditorWindow pane, bool killIfEmpty, bool sendEvents = tru private void UpdateWindowTitle(EditorWindow w) { if (w && w.m_Parent && w.m_Parent.window && w.titleContent != null) - { - var projectName = EditorApplication.isTemporaryProject ? PlayerSettings.productName : Path.GetFileName(Path.GetDirectoryName(Application.dataPath)); - w.m_Parent.window.title = w.titleContent.text + " - " + projectName; - } + w.m_Parent.window.title = w.titleContent.text; } private void KillIfEmpty() @@ -320,6 +317,7 @@ protected bool floatingWindow protected override void OldOnGUI() { + var oldLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.ResetGUIState(); // Exit if the window was destroyed after entering play mode or on domain-reload. @@ -362,6 +360,7 @@ protected override void OldOnGUI() EditorGUI.ShowRepaints(); Highlighter.ControlHighlightGUI(this); + EditorGUIUtility.labelWidth = oldLabelWidth; } private void DrawView(Rect viewRect, Rect dockAreaRect) @@ -1097,7 +1096,8 @@ protected override void OldOnGUI() if (Event.current.type == EventType.ContextClick && backRect.Contains(Event.current.mousePosition) && !ContainerWindow.s_Modal) PopupGenericMenu(actualView, new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y, 0, 0)); - ShowGenericMenu(position.width - GetGenericMenuLeftOffset(true), backRect.yMin + Styles.genericMenuTopOffset); + // GetGenericMenuLeftOffset false because maximized window are not floating windows + ShowGenericMenu(position.width - GetGenericMenuLeftOffset(false), backRect.yMin + Styles.genericMenuTopOffset); const float topBottomPadding = 0f; Rect viewRect = maximizedViewRect; diff --git a/Editor/Mono/GUI/ManagedDebuggerToggle.cs b/Editor/Mono/GUI/ManagedDebuggerToggle.cs index 0ac2d3c6f5..a7536ca4fa 100644 --- a/Editor/Mono/GUI/ManagedDebuggerToggle.cs +++ b/Editor/Mono/GUI/ManagedDebuggerToggle.cs @@ -15,46 +15,32 @@ internal class ManagedDebuggerToggle private readonly GUIContent m_DebuggerEnabledContent; private readonly PopupLocation[] m_PopupLocation; - private const int k_Width = 36; - private const int k_Height = 19; - private const int k_MarginX = 0; - private const int k_MarginY = 0; - public ManagedDebuggerToggle() { - m_DebuggerAttachedContent = EditorGUIUtility.TrIconContent("DebuggerAttached"); - m_DebuggerDisabledContent = EditorGUIUtility.TrIconContent("DebuggerDisabled"); - m_DebuggerEnabledContent = EditorGUIUtility.TrIconContent("DebuggerEnabled"); + m_DebuggerAttachedContent = EditorGUIUtility.TrIconContent("DebuggerAttached", "Debugger Attached"); + m_DebuggerDisabledContent = EditorGUIUtility.TrIconContent("DebuggerDisabled", "Debugger Disabled"); + m_DebuggerEnabledContent = EditorGUIUtility.TrIconContent("DebuggerEnabled", "Debugger Enabled"); m_PopupLocation = new[] { PopupLocation.AboveAlignRight }; } - public void OnGUI(float x, float y) + public void OnGUI() { using (new EditorGUI.DisabledScope(!ManagedDebugger.isEnabled)) { var codeOptimization = CompilationPipeline.codeOptimization; var debuggerAttached = ManagedDebugger.isAttached; - var debuggerContent = GetDebuggerContent(debuggerAttached, codeOptimization); - var buttonArea = new Rect(x + k_MarginX, y + k_MarginY, k_Width, k_Height); + var content = GetDebuggerContent(debuggerAttached, codeOptimization); - if (EditorGUI.DropdownButton(buttonArea, debuggerContent, FocusType.Passive, EditorStyles.toolbarDropDown)) + var style = AppStatusBar.Styles.statusIcon; + var rect = GUILayoutUtility.GetRect(content, style); + if (GUI.Button(rect, content, style)) { - PopupWindow.Show(buttonArea, new ManagedDebuggerWindow(codeOptimization), m_PopupLocation); + PopupWindow.Show(rect, new ManagedDebuggerWindow(codeOptimization), m_PopupLocation); GUIUtility.ExitGUI(); } } } - public float GetWidth() - { - return k_Width + (k_MarginX << 1); - } - - public float GetHeight() - { - return k_Height + (k_MarginY << 1); - } - private GUIContent GetDebuggerContent(bool debuggerAttached, CodeOptimization optimization) { if (CodeOptimization.Debug == optimization) diff --git a/Editor/Mono/GUI/PackageImportTreeView.cs b/Editor/Mono/GUI/PackageImportTreeView.cs index 7a7f949a90..1e5dc057d6 100644 --- a/Editor/Mono/GUI/PackageImportTreeView.cs +++ b/Editor/Mono/GUI/PackageImportTreeView.cs @@ -369,7 +369,7 @@ override public void OnRowGUI(Rect rowRect, TreeViewItem tvItem, int row, bool s { Rect labelRect = new Rect(rowRect.xMax - 58, rowRect.y, rowRect.height, rowRect.height); EditorGUIUtility.SetIconSize(new Vector2(rowRect.height, rowRect.height)); - GUI.Label(labelRect, Constants.badgeWarn); + GUI.Label(labelRect, Constants.badgeWarn, Constants.paddinglessStyle); EditorGUIUtility.SetIconSize(Vector2.zero); } diff --git a/Editor/Mono/GUI/PopupWindow.cs b/Editor/Mono/GUI/PopupWindow.cs index a893de091c..e998c60dac 100644 --- a/Editor/Mono/GUI/PopupWindow.cs +++ b/Editor/Mono/GUI/PopupWindow.cs @@ -51,6 +51,22 @@ internal static void Show(Rect activatorRect, PopupWindowContent windowContent, // Shown on top of any previous windows internal static void Show(Rect activatorRect, PopupWindowContent windowContent, PopupLocation[] locationPriorityOrder, ShowMode showMode) { + // If we already have a popup window showing this type of content, then just close + // the existing one. + var existingWindows = Resources.FindObjectsOfTypeAll(typeof(PopupWindow)); + if (existingWindows != null && existingWindows.Length > 0) + { + var existingPopup = existingWindows[0] as PopupWindow; + if (existingPopup != null) + { + if (existingPopup.m_WindowContent.GetType() == windowContent.GetType()) + { + existingPopup.CloseWindow(); + return; + } + } + } + if (ShouldShowWindow(activatorRect)) { PopupWindow win = CreateInstance(); diff --git a/Editor/Mono/GUI/PopupWindowWithoutFocus.cs b/Editor/Mono/GUI/PopupWindowWithoutFocus.cs index 0641e89087..2fedccf626 100644 --- a/Editor/Mono/GUI/PopupWindowWithoutFocus.cs +++ b/Editor/Mono/GUI/PopupWindowWithoutFocus.cs @@ -72,6 +72,12 @@ static bool OnGlobalMouseOrKeyEvent(EventType type, KeyCode keyCode, Vector2 mou if (s_PopupWindowWithoutFocus == null) return false; + if (type == EventType.MouseDown && !s_PopupWindowWithoutFocus.position.Contains(mousePosition)) + { + s_PopupWindowWithoutFocus.Close(); + return false; + } + if (type == EventType.KeyDown && keyCode == KeyCode.Escape) { s_PopupWindowWithoutFocus.Close(); diff --git a/Editor/Mono/GUI/RenameOverlay.cs b/Editor/Mono/GUI/RenameOverlay.cs index 019fddb9c7..b8c704861c 100644 --- a/Editor/Mono/GUI/RenameOverlay.cs +++ b/Editor/Mono/GUI/RenameOverlay.cs @@ -48,9 +48,6 @@ internal class RenameOverlay [System.NonSerialized] bool m_UndoRedoWasPerformed; - [System.NonSerialized] - DelayedCallback m_DelayedCallback; - string k_RenameOverlayFocusName = "RenameOverlayField"; // property interface @@ -84,7 +81,7 @@ public bool BeginRename(string name, int userData, float delay) m_ClientGUIView = GUIView.current; if (delay > 0f) - m_DelayedCallback = new DelayedCallback(BeginRenameInternalCallback, delay); + EditorApplication.CallDelayed(BeginRenameInternalCallback, delay); else BeginRenameInternalCallback(); return true; @@ -109,8 +106,7 @@ public void EndRename(bool acceptChanges) return; Undo.undoRedoPerformed -= UndoRedoWasPerformed; - if (m_DelayedCallback != null) - m_DelayedCallback.Clear(); + EditorApplication.update -= BeginRenameInternalCallback; RemoveMessage(); @@ -416,7 +412,7 @@ void Update() var callback = m_Callback; Clear(); - callback(); + callback?.Invoke(); } } diff --git a/Editor/Mono/GUI/Toolbar.cs b/Editor/Mono/GUI/Toolbar.cs index caa66c2136..e5e17b4626 100644 --- a/Editor/Mono/GUI/Toolbar.cs +++ b/Editor/Mono/GUI/Toolbar.cs @@ -135,6 +135,8 @@ protected override void OnEnable() } PositionChanged(this); + + m_EventInterests.wantsLessLayoutEvents = true; } protected override void OnDisable() diff --git a/Editor/Mono/GUIView.bindings.cs b/Editor/Mono/GUIView.bindings.cs index 9887c2772c..72f0992bcc 100644 --- a/Editor/Mono/GUIView.bindings.cs +++ b/Editor/Mono/GUIView.bindings.cs @@ -36,6 +36,7 @@ internal partial class GUIView internal extern bool mouseRayInvisible {[NativeMethod("IsMouseRayInvisible")] get; [NativeMethod("SetMouseRayInvisible")] set; } internal extern bool disableInputEvents {[NativeMethod("AreInputEventsDisabled")] get; [NativeMethod("SetDisableInputEvents")] set; } + internal extern bool hdrActive {[NativeMethod("IsHDRActive")] get; } internal extern void SetTitle(string title); internal extern void AddToAuxWindowList(); diff --git a/Editor/Mono/GUIView.cs b/Editor/Mono/GUIView.cs index 8bf86132f3..23303d0b81 100644 --- a/Editor/Mono/GUIView.cs +++ b/Editor/Mono/GUIView.cs @@ -23,10 +23,11 @@ internal partial class GUIView : View, IWindowModel int m_DepthBufferBits = 0; int m_AntiAliasing = 1; - EventInterests m_EventInterests; bool m_AutoRepaintOnSceneChange = false; private IWindowBackend m_WindowBackend; + protected EventInterests m_EventInterests; + internal bool SendEvent(Event e) { int depth = SavedGUIState.Internal_GetGUIDepth(); diff --git a/Editor/Mono/GameView/GameView.cs b/Editor/Mono/GameView/GameView.cs index f9f767769e..a8db2a9d8d 100644 --- a/Editor/Mono/GameView/GameView.cs +++ b/Editor/Mono/GameView/GameView.cs @@ -276,6 +276,7 @@ void InitializeZoomArea() public void OnEnable() { + wantsLessLayoutEvents = true; prevSizeGroupType = (int)currentSizeGroupType; titleContent = GetLocalizedTitleContent(); UpdateZoomAreaAndParent(); @@ -717,16 +718,6 @@ private void OnEditorModeChanged(ModeService.ModeChangedArgs args) Repaint(); } - protected override string SerializeView() - { - return EditorJsonUtility.ToJson(this); - } - - protected override void DeserializeView(string serializedView) - { - EditorJsonUtility.FromJsonOverwrite(serializedView, this); - } - private void OnGUI() { if (position.size * EditorGUIUtility.pixelsPerPoint != m_LastWindowPixelSize) // pixelsPerPoint only reliable in OnGUI() diff --git a/Editor/Mono/HostView.cs b/Editor/Mono/HostView.cs index be51571f05..5465ec202a 100644 --- a/Editor/Mono/HostView.cs +++ b/Editor/Mono/HostView.cs @@ -173,6 +173,10 @@ protected override void OnDisable() EditorPrefs.onValueWasUpdated -= PlayModeTintColorChangedCallback; base.OnDisable(); DeregisterSelectedPane(clearActualView: false, sendEvents: true); + // Host views are destroyed in the middle of an OnGUI loop, so we need to ensure that we're not invoking + // OnGUI on destroyed instances. + m_OnGUI = null; + m_Update = null; } private void HandleSplitView() @@ -406,6 +410,9 @@ private string GetActualViewName() public void InvokeOnGUI(Rect onGUIPosition, Rect viewRect) { + if (!this) + return; + DoWindowDecorationStart(); BeginOffsetArea(viewRect, GUIContent.none, Styles.tabWindowBackground); @@ -641,9 +648,6 @@ internal float GetExtraButtonsWidth() internal const float k_iconMargin = 1f; protected void ShowGenericMenu(float leftOffset, float topOffset) { - if (Event.current.isKey) - return; - Rect paneMenu = new Rect(leftOffset, topOffset, Styles.paneOptions.fixedWidth, Styles.paneOptions.fixedHeight); if (EditorGUI.DropdownButton(paneMenu, GUIContent.none, FocusType.Passive, Styles.paneOptions)) PopupGenericMenu(m_ActualView, paneMenu); diff --git a/Editor/Mono/Inspector/AnimationClipEditor.cs b/Editor/Mono/Inspector/AnimationClipEditor.cs index 950195b5ab..f71ece4c2e 100644 --- a/Editor/Mono/Inspector/AnimationClipEditor.cs +++ b/Editor/Mono/Inspector/AnimationClipEditor.cs @@ -36,7 +36,7 @@ internal static void EditWithImporter(AnimationClip clip) clipIndex = i; } - EditorPrefs.SetInt("ModelImporterClipEditor.ActiveClipIndex", clipIndex); + EditorPrefs.SetInt(ModelImporterClipEditor.ActiveClipIndex, clipIndex); } } diff --git a/Editor/Mono/Inspector/AssetBundleNameGUI.cs b/Editor/Mono/Inspector/AssetBundleNameGUI.cs index 76bef7fdea..daf3b35432 100644 --- a/Editor/Mono/Inspector/AssetBundleNameGUI.cs +++ b/Editor/Mono/Inspector/AssetBundleNameGUI.cs @@ -33,6 +33,7 @@ private static class Styles public void OnAssetBundleNameGUI(IEnumerable assets) { + float oldLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 90f; Rect bundleRect = EditorGUILayout.GetControlRect(true, EditorGUI.kSingleLineHeight); @@ -56,6 +57,8 @@ public void OnAssetBundleNameGUI(IEnumerable assets) AssetBundleTextField(variantRect, id, assets, true); else AssetBundlePopup(variantRect, id, assets, true); + + EditorGUIUtility.labelWidth = oldLabelWidth; } private void ShowNewAssetBundleField(bool isVariant) diff --git a/Editor/Mono/Inspector/CameraEditor.cs b/Editor/Mono/Inspector/CameraEditor.cs index 3a8bb2fbb8..518f8933db 100644 --- a/Editor/Mono/Inspector/CameraEditor.cs +++ b/Editor/Mono/Inspector/CameraEditor.cs @@ -788,7 +788,6 @@ public virtual void OnOverlayGUI(Object target, SceneView sceneView) { // setup camera and render previewCamera.CopyFrom(c); - previewCamera.cameraType = CameraType.Preview; // make sure the preview camera is rendering the same stage as the SceneView is if (sceneView.overrideSceneCullingMask != 0) diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspector.cs b/Editor/Mono/Inspector/GraphicsSettingsInspector.cs index 5ac49e7b8c..97c42f8a8a 100644 --- a/Editor/Mono/Inspector/GraphicsSettingsInspector.cs +++ b/Editor/Mono/Inspector/GraphicsSettingsInspector.cs @@ -149,7 +149,6 @@ public override void OnInspectorGUI() bool usingSRP = GraphicsSettings.currentRenderPipeline != null; - if (usingSRP) EditorGUILayout.HelpBox("A Scriptable Render Pipeline is in use, some settings will not be used and are hidden", MessageType.Info); @@ -164,7 +163,17 @@ public override void OnInspectorGUI() float labelWidth = EditorGUIUtility.labelWidth; - TierSettingsGUI(); + // Hide tier settings for SRPs and close tier settings window if open + if (usingSRP) + { + TierSettingsWindow window = TierSettingsWindow.GetInstance(); + if (window != null) + window.Close(); + } + else + { + TierSettingsGUI(); + } EditorGUIUtility.labelWidth = labelWidth; diff --git a/Editor/Mono/Inspector/InspectorWindow.cs b/Editor/Mono/Inspector/InspectorWindow.cs index 07325cd6fe..7a8b440468 100644 --- a/Editor/Mono/Inspector/InspectorWindow.cs +++ b/Editor/Mono/Inspector/InspectorWindow.cs @@ -399,7 +399,7 @@ private void RestoreLockStateFromSerializedData() m_Tracker.SetObjectsLockedByThisTracker(m_ObjectsLockedBeforeSerialization); // since this method likely got called during OnEnable, and rebuilding the tracker could call OnDisable on all Editors, // some of which might not have gotten their enable yet, the rebuilding needs to happen delayed in EditorApplication.update - new DelayedCallback(tracker.RebuildIfNecessary, 0f); + EditorApplication.CallDelayed(tracker.RebuildIfNecessary, 0f); } internal static bool AddInspectorWindow(InspectorWindow window) diff --git a/Editor/Mono/Inspector/LODGroupEditor.cs b/Editor/Mono/Inspector/LODGroupEditor.cs index b8edfc9e94..d4d1b7b255 100644 --- a/Editor/Mono/Inspector/LODGroupEditor.cs +++ b/Editor/Mono/Inspector/LODGroupEditor.cs @@ -708,13 +708,28 @@ private static void UpdateCamera(float desiredPercentage, LODGroup group) var worldReferencePoint = LODUtility.CalculateWorldReferencePoint(group); var percentage = Mathf.Max(desiredPercentage / QualitySettings.lodBias, 0.000001f); + var sceneView = SceneView.lastActiveSceneView; + var sceneCamera = sceneView.camera; + // Figure out a distance based on the percentage - var distance = LODUtility.CalculateDistance(SceneView.lastActiveSceneView.camera, percentage, group); + var distance = LODUtility.CalculateDistance(sceneCamera, percentage, group); - if (SceneView.lastActiveSceneView.camera.orthographic) - distance *= Mathf.Sqrt(2 * SceneView.lastActiveSceneView.camera.aspect); + // We need to do inverse of SceneView.cameraDistance: + // given the distance, need to figure out "size" to focus the scene view on. + float size; + if (sceneCamera.orthographic) + { + size = distance; + if (sceneCamera.aspect < 1.0) + size *= sceneCamera.aspect; + } + else + { + var fov = sceneCamera.fieldOfView; + size = distance * Mathf.Sin(fov * 0.5f * Mathf.Deg2Rad); + } - SceneView.lastActiveSceneView.LookAtDirect(worldReferencePoint, SceneView.lastActiveSceneView.camera.transform.rotation, distance); + SceneView.lastActiveSceneView.LookAtDirect(worldReferencePoint, sceneCamera.transform.rotation, size); } private void UpdateSelectedLODFromCamera(IEnumerable lods, float cameraPercent) @@ -1091,7 +1106,8 @@ private void SendPercentagesToLightmapScale() lodRenderers.Add(new LODLightmapScale(pixelHeight, renderersAtLOD)); } - for (var i = 0; i < m_NumberOfLODs; i++) + // set from least detailed to most detailed, as renderers can be in multiple layers + for (var i = m_NumberOfLODs - 1; i >= 0; i--) { SetLODLightmapScale(lodRenderers[i]); } diff --git a/Editor/Mono/Inspector/MaterialEditor.cs b/Editor/Mono/Inspector/MaterialEditor.cs index f55cf81384..167cc51040 100644 --- a/Editor/Mono/Inspector/MaterialEditor.cs +++ b/Editor/Mono/Inspector/MaterialEditor.cs @@ -1971,9 +1971,15 @@ private void StreamRenderResources() if (!stackTextures.ContainsKey(stackId)) { //Get the dimension of the texture stack. This can be different from the texture dimensions. - int width, height; - if (VirtualTexturing.EditorHelpers.GetTextureStackSize(mat, stackId, out width, out height)) + try + { + int width, height; + VirtualTexturing.System.GetTextureStackSize(mat, stackId, out width, out height); stackTextures[stackId] = Math.Max(width, height); + } + catch + { + } } } } diff --git a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs index 979a8965a4..af99e42799 100644 --- a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs +++ b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs @@ -14,6 +14,7 @@ using System.Linq; using UnityEditor.Modules; using UnityEngine.Events; +using UnityEngine.SceneManagement; using GraphicsDeviceType = UnityEngine.Rendering.GraphicsDeviceType; using TargetAttributes = UnityEditor.BuildTargetDiscovery.TargetAttributes; using UnityEngine.Rendering; @@ -78,6 +79,7 @@ class SettingsContent public static readonly GUIContent optimizationTitle = EditorGUIUtility.TrTextContent("Optimization"); public static readonly GUIContent loggingTitle = EditorGUIUtility.TrTextContent("Stack Trace*"); public static readonly GUIContent legacyTitle = EditorGUIUtility.TrTextContent("Legacy"); + public static readonly GUIContent legacyXRTitle = EditorGUIUtility.TrTextContent("XR Settings (Deprecated)"); public static readonly GUIContent publishingSettingsTitle = EditorGUIUtility.TrTextContent("Publishing Settings"); public static readonly GUIContent bakeCollisionMeshes = EditorGUIUtility.TrTextContent("Prebake Collision Meshes*", "Bake collision data into the meshes on build time"); @@ -1074,16 +1076,51 @@ private ChangeGraphicsApiAction CheckApplyGraphicsAPIList(BuildTarget target, bo // If we're changing the first API for relevant editor, this will cause editor to switch: ask for scene save & confirmation if (firstEntryChanged && WillEditorUseFirstGraphicsAPI(target)) { - if (EditorUtility.DisplayDialog("Changing editor graphics device", - "You've changed the active graphics API. This requires a restart of the Editor.", - "Restart Editor", "Not now")) + // If we have dirty scenes we need to save or discard changes before we restart editor. + // Otherwise user will get a dialog later on where they can click cancel and put editor in a bad device state. + var dirtyScenes = new List(); + for (int i = 0; i < EditorSceneManager.sceneCount; ++i) { - doRestart = true; + var scene = EditorSceneManager.GetSceneAt(i); + if (scene.isDirty) + dirtyScenes.Add(scene); + } + if (dirtyScenes.Count != 0) + { + var result = EditorUtility.DisplayDialogComplex("Changing editor graphics API", + "You've changed the active graphics API. This requires a restart of the Editor. Do you want to save the Scene when restarting?", + "Save and Restart", "Discard Changes and Restart", "Cancel Changing API"); + if (result == 2) + { + doRestart = false; // Cancel was selected + } + else + { + doRestart = true; + if (result == 0) // Save and Restart was selected + { + for (int i = 0; i < dirtyScenes.Count; ++i) + EditorSceneManager.SaveScene(dirtyScenes[i]); + } + else // Discard Changes and Restart was selected + { + for (int i = 0; i < dirtyScenes.Count; ++i) + EditorSceneManager.ClearSceneDirtiness(dirtyScenes[i]); + } + } } else - doRestart = false; + { + doRestart = EditorUtility.DisplayDialog("Changing editor graphics API", + "You've changed the active graphics API. This requires a restart of the Editor.", + "Restart Editor", "Not now"); + } + return new ChangeGraphicsApiAction(doRestart, doRestart); + } + else + { + return new ChangeGraphicsApiAction(true, false); } - return new ChangeGraphicsApiAction(doRestart, doRestart); } private void ApplyChangeGraphicsApiAction(BuildTarget target, GraphicsDeviceType[] apis, ChangeGraphicsApiAction action) @@ -2328,6 +2365,8 @@ private void OtherSectionLegacyGUI(BuildTargetGroup targetGroup) // ARCore - legacy way to enable if (BuildTargetDiscovery.PlatformGroupHasVRFlag(targetGroup, BuildTargetDiscovery.VRAttributes.SupportTango)) { + EditorGUILayout.Space(); + GUILayout.Label(SettingsContent.legacyXRTitle, EditorStyles.boldLabel); EditorGUILayout.PropertyField(m_AndroidEnableTango, EditorGUIUtility.TrTextContent("ARCore Supported")); } diff --git a/Editor/Mono/Inspector/QualitySettingsEditor.cs b/Editor/Mono/Inspector/QualitySettingsEditor.cs index d2710ed199..6290b5d7e7 100644 --- a/Editor/Mono/Inspector/QualitySettingsEditor.cs +++ b/Editor/Mono/Inspector/QualitySettingsEditor.cs @@ -314,7 +314,10 @@ private void HandleAddRemoveQualitySetting(ref int selectedLevel, Dictionary= 0) { if (m_DeleteLevel < selectedLevel || m_DeleteLevel == m_QualitySettingsProperty.arraySize - 1) + { selectedLevel = Mathf.Max(0, selectedLevel - 1); + QualitySettings.SetQualityLevel(selectedLevel); + } //Always ensure there is one quality setting if (m_QualitySettingsProperty.arraySize > 1 && m_DeleteLevel >= 0 && m_DeleteLevel < m_QualitySettingsProperty.arraySize) @@ -474,6 +477,10 @@ public override void OnInspectorGUI() var settings = GetQualitySettings(); var defaults = GetDefaultQualityForPlatforms(); var selectedLevel = QualitySettings.GetQualityLevel(); + if (selectedLevel >= m_QualitySettingsProperty.arraySize) + { + selectedLevel = m_QualitySettingsProperty.arraySize - 1; + } EditorGUI.BeginChangeCheck(); selectedLevel = DoQualityLevelSelection(selectedLevel, settings, defaults); diff --git a/Editor/Mono/Inspector/RendererEditorBase.cs b/Editor/Mono/Inspector/RendererEditorBase.cs index 6ef7c24793..f2e54aa962 100644 --- a/Editor/Mono/Inspector/RendererEditorBase.cs +++ b/Editor/Mono/Inspector/RendererEditorBase.cs @@ -452,8 +452,16 @@ protected void DrawMaterials() { EditorGUI.indentLevel++; + EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_MaterialsSize); + if (EditorGUI.EndChangeCheck()) + { + serializedObject.ApplyModifiedProperties(); + GUIUtility.ExitGUI(); + // stop processing the current event as the size of the list has changed + } + for (int i = 0; i < m_MaterialsSize.intValue; i++) { EditorGUILayout.PropertyField(m_Materials.GetArrayElementAtIndex(i)); diff --git a/Editor/Mono/Inspector/RendererLightingSettings.cs b/Editor/Mono/Inspector/RendererLightingSettings.cs index 4ca57a402e..58e124427d 100644 --- a/Editor/Mono/Inspector/RendererLightingSettings.cs +++ b/Editor/Mono/Inspector/RendererLightingSettings.cs @@ -134,7 +134,7 @@ private bool isPrefabAsset private float CalcLODScale(bool isMeshRenderer) { float lodScale = 1.0f; - if (isMeshRenderer) + if (isMeshRenderer && (m_Renderers != null) && (m_Renderers.Length > 0)) { lodScale = LightmapVisualization.GetLightmapLODLevelScale(m_Renderers[0]); for (int i = 1; i < m_Renderers.Length; i++) @@ -288,24 +288,27 @@ public void RenderSettings(bool showLightmapSettings, bool showshadowBias) RendererUVSettings(); } - ShowAtlasGUI(m_Renderers[0].GetInstanceID()); - ShowRealtimeLMGUI(m_Renderers[0]); + if ((m_Renderers != null) && (m_Renderers.Length > 0)) + { + ShowAtlasGUI(m_Renderers[0].GetInstanceID(), true); + ShowRealtimeLMGUI(m_Renderers[0]); - if (Lightmapping.HasZeroAreaMesh(m_Renderers[0])) - EditorGUILayout.HelpBox(Styles.ZeroAreaPackingMesh.text, MessageType.Warning); + if (Lightmapping.HasZeroAreaMesh(m_Renderers[0])) + EditorGUILayout.HelpBox(Styles.ZeroAreaPackingMesh.text, MessageType.Warning); - DisplayMeshWarning(); + DisplayMeshWarning(); - if (showEnlightenSettings) - { - if (Lightmapping.HasClampedResolution(m_Renderers[0])) - EditorGUILayout.HelpBox(Styles.ClampedPackingResolution.text, MessageType.Warning); - } + if (showEnlightenSettings) + { + if (Lightmapping.HasClampedResolution(m_Renderers[0])) + EditorGUILayout.HelpBox(Styles.ClampedPackingResolution.text, MessageType.Warning); + } - if (showProgressiveSettings) - { - if (Lightmapping.HasUVOverlaps(m_Renderers[0])) - EditorGUILayout.HelpBox(Styles.UVOverlap.text, MessageType.Warning); + if (showProgressiveSettings) + { + if (Lightmapping.HasUVOverlaps(m_Renderers[0])) + EditorGUILayout.HelpBox(Styles.UVOverlap.text, MessageType.Warning); + } } EditorGUI.indentLevel -= 1; @@ -369,11 +372,14 @@ public void RenderTerrainSettings() LightmapParametersGUI(m_LightmapParameters, Styles.LightmapParameters); - if (GUI.enabled && m_Terrains.Length == 1 && m_Terrains[0].terrainData != null) - ShowBakePerformanceWarning(m_Terrains[0]); + if ((m_Terrains != null) && (m_Terrains.Length > 0)) + { + if (GUI.enabled && m_Terrains.Length == 1 && m_Terrains[0].terrainData != null) + ShowBakePerformanceWarning(m_Terrains[0]); - ShowAtlasGUI(m_Terrains[0].GetInstanceID()); - ShowRealtimeLMGUI(m_Terrains[0]); + ShowAtlasGUI(m_Terrains[0].GetInstanceID(), false); + ShowRealtimeLMGUI(m_Terrains[0]); + } EditorGUI.indentLevel -= 1; } @@ -441,14 +447,14 @@ void ShowClampedSizeInLightmapGUI(float lightmapScale, float cachedSurfaceArea, EditorGUILayout.HelpBox(Styles.ClampedSize.text, MessageType.Info); } - void LightmapScaleGUI(bool meshRenderer, GUIContent title, bool isSSD) + void LightmapScaleGUI(bool isMeshRenderer, GUIContent title, bool isSSD) { // SSDs (with the exception of those being computed with Enlighten) do not end up in a lightmap, // therefore we do not show clamping information. if (isSSD && Lightmapping.GetLightingSettingsOrDefaultsFallback().lightmapper != LightingSettings.Lightmapper.Enlighten) return; - float lodScale = CalcLODScale(meshRenderer); + float lodScale = CalcLODScale(isMeshRenderer); float lightmapScale = lodScale * m_LightmapScale.floatValue; Rect rect = EditorGUILayout.GetControlRect(); @@ -459,26 +465,32 @@ void LightmapScaleGUI(bool meshRenderer, GUIContent title, bool isSSD) m_LightmapScale.floatValue = Mathf.Max(lightmapScale / Mathf.Max(lodScale, float.Epsilon), 0.0f); EditorGUI.EndProperty(); - float cachedSurfaceArea; + float cachedSurfaceArea = 0.0f; - if (meshRenderer) + if (isMeshRenderer) { - lightmapScale = lightmapScale * LightmapVisualization.GetLightmapLODLevelScale(m_Renderers[0]); + if ((m_Renderers != null) && (m_Renderers.Length > 0)) + { + lightmapScale = lightmapScale * LightmapVisualization.GetLightmapLODLevelScale(m_Renderers[0]); - // tell the user if the object's size in lightmap has reached the max atlas size - cachedSurfaceArea = InternalMeshUtil.GetCachedMeshSurfaceArea((MeshRenderer)m_Renderers[0]); + // tell the user if the object's size in lightmap has reached the max atlas size + cachedSurfaceArea = InternalMeshUtil.GetCachedMeshSurfaceArea((MeshRenderer)m_Renderers[0]); + } } else //terrain { // tell the user if the object's size in lightmap has reached the max atlas size - var terrainData = m_Terrains[0].terrainData; - cachedSurfaceArea = terrainData != null ? terrainData.size.x * terrainData.size.z : 0; + if ((m_Terrains != null) && (m_Terrains.Length > 0)) + { + var terrainData = m_Terrains[0].terrainData; + cachedSurfaceArea = terrainData != null ? terrainData.size.x * terrainData.size.z : 0.0f; + } } ShowClampedSizeInLightmapGUI(lightmapScale, cachedSurfaceArea, isSSD); } - void ShowAtlasGUI(int instanceID) + void ShowAtlasGUI(int instanceID, bool isMeshRenderer) { if (m_LightmapIndex == null) return; @@ -513,11 +525,15 @@ void ShowAtlasGUI(int instanceID) var settings = Lightmapping.GetLightingSettingsOrDefaultsFallback(); - float lightmapResolution = settings.lightmapResolution * CalcLODScale(true) * m_LightmapScale.floatValue; - Transform transform = m_Renderers[0].GetComponent(); - float lightmapObjectScale = System.Math.Min(System.Math.Min(transform.localScale.x, transform.localScale.y), transform.localScale.z); - GUILayout.Label(Styles.LightmapResolution.text + ": " + lightmapResolution.ToString(CultureInfo.InvariantCulture.NumberFormat)); - GUILayout.Label(Styles.LightmapObjectScale.text + ": " + lightmapObjectScale.ToString(CultureInfo.InvariantCulture.NumberFormat)); + float lightmapResolution = settings.lightmapResolution * CalcLODScale(isMeshRenderer) * m_LightmapScale.floatValue; + + if (isMeshRenderer && (m_Renderers != null) && (m_Renderers.Length > 0)) + { + Transform transform = m_Renderers[0].GetComponent(); + float lightmapObjectScale = System.Math.Min(System.Math.Min(transform.localScale.x, transform.localScale.y), transform.localScale.z); + GUILayout.Label(Styles.LightmapResolution.text + ": " + lightmapResolution.ToString(CultureInfo.InvariantCulture.NumberFormat)); + GUILayout.Label(Styles.LightmapObjectScale.text + ": " + lightmapObjectScale.ToString(CultureInfo.InvariantCulture.NumberFormat)); + } GUILayout.EndVertical(); GUILayout.FlexibleSpace(); @@ -828,6 +844,9 @@ static public bool LightmapParametersGUI(SerializedProperty prop, GUIContent con void ShowTerrainChunks(Terrain[] terrains) { + if (terrains == null) + return; + int terrainChunksX = 0, terrainChunksY = 0; foreach (var terrain in terrains) { diff --git a/Editor/Mono/InternalEditorUtility.cs b/Editor/Mono/InternalEditorUtility.cs index 1cc585c473..4da4c8c808 100644 --- a/Editor/Mono/InternalEditorUtility.cs +++ b/Editor/Mono/InternalEditorUtility.cs @@ -45,7 +45,7 @@ public static Texture2D FindIconForFile(string fileName) case "mixer": return EditorGUIUtility.FindTexture(typeof(UnityEditor.Audio.AudioMixerController)); case "uxml": return EditorGUIUtility.FindTexture(typeof(UnityEngine.UIElements.VisualTreeAsset)); case "uss": return EditorGUIUtility.FindTexture(typeof(StyleSheet)); - case "lighting": return EditorGUIUtility.FindTexture(typeof(UnityEditor.LightingSettings)); + case "lighting": return EditorGUIUtility.FindTexture(typeof(UnityEngine.LightingSettings)); case "ttf": case "otf": case "fon": case "fnt": return EditorGUIUtility.FindTexture(typeof(Font)); diff --git a/Editor/Mono/ObjectListArea.cs b/Editor/Mono/ObjectListArea.cs index 6dc65c4b48..e414e2ca8f 100644 --- a/Editor/Mono/ObjectListArea.cs +++ b/Editor/Mono/ObjectListArea.cs @@ -115,6 +115,8 @@ static GUIStyle GetStyle(string styleName) Vector2 m_LastScrollPosition = new Vector2(0, 0); double LastScrollTime = 0; + public bool selectedAssetStoreAsset; + internal Texture m_SelectedObjectIcon = null; LocalGroup m_LocalAssets; @@ -802,11 +804,23 @@ public void InitSelection(int[] selectedInstanceIDs) { m_State.m_LastClickedInstanceID = 0; } + + if (Selection.activeObject == null || Selection.activeObject.GetType() != typeof(AssetStoreAssetInspector)) + { + selectedAssetStoreAsset = false; + AssetStoreAssetSelection.Clear(); + } } void SetSelection(AssetStoreAsset assetStoreResult, bool doubleClicked) { m_State.m_SelectedInstanceIDs.Clear(); + + selectedAssetStoreAsset = true; + AssetStoreAssetSelection.Clear(); + AssetStorePreviewManager.CachedAssetStoreImage item = AssetStorePreviewManager.TextureFromUrl(assetStoreResult.staticPreviewURL, assetStoreResult.name, gridSize, s_Styles.resultsGridLabel, s_Styles.resultsGrid, true); + Texture2D lowresPreview = item.image; + AssetStoreAssetSelection.AddAsset(assetStoreResult, lowresPreview); if (m_ItemSelectedCallback != null) { Repaint(); @@ -1137,6 +1151,30 @@ int GetSelectedAssetIdx() int offsetIdx = m_LocalAssets.IndexOf(m_State.m_LastClickedInstanceID); if (offsetIdx != -1) return offsetIdx; + + offsetIdx = m_LocalAssets.m_Grid.rows * m_LocalAssets.m_Grid.columns; + + // Project or builtin asset not selected. Check asset store asset. + if (AssetStoreAssetSelection.Count == 0) + return -1; + + AssetStoreAsset asset = AssetStoreAssetSelection.GetFirstAsset(); + if (asset == null) + return -1; + int assetID = asset.id; + + foreach (AssetStoreGroup g in m_StoreAssets) + { + if (!g.Visible) + continue; + + int idx = g.IndexOf(assetID); + if (idx != -1) + return offsetIdx + idx; + + offsetIdx += g.m_Grid.rows * g.m_Grid.columns; + } + return -1; } diff --git a/Editor/Mono/ObjectListAssetStoreGroup.cs b/Editor/Mono/ObjectListAssetStoreGroup.cs index b9927dd4d0..a5ca8d2cc0 100644 --- a/Editor/Mono/ObjectListAssetStoreGroup.cs +++ b/Editor/Mono/ObjectListAssetStoreGroup.cs @@ -93,7 +93,10 @@ protected override void DrawInternal(int itemIdx, int endItem, float yOffset) m_Owner.SetSelection(m_Assets[itemIdx], clicks == 2); if (isRepaintEvent) - DrawLabel(r, m_Assets[itemIdx], false); + { + bool selected = !AssetStoreAssetSelection.Empty && AssetStoreAssetSelection.ContainsAsset(m_Assets[itemIdx].id); + DrawLabel(r, m_Assets[itemIdx], selected); + } } } else @@ -124,7 +127,8 @@ protected override void DrawInternal(int itemIdx, int endItem, float yOffset) for (; itemIdx < endItem && itemIdx < endContainerItem; itemIdx++) { r = m_Grid.CalcRect(itemIdx, yOffset); - DrawLabel(r, m_Assets[itemIdx], false); + bool selected = !AssetStoreAssetSelection.Empty && AssetStoreAssetSelection.ContainsAsset(m_Assets[itemIdx].id); + DrawLabel(r, m_Assets[itemIdx], selected); } } } diff --git a/Editor/Mono/PlayModeView/PlayModeView.cs b/Editor/Mono/PlayModeView/PlayModeView.cs index ceb42ce846..da4cee0ecb 100644 --- a/Editor/Mono/PlayModeView/PlayModeView.cs +++ b/Editor/Mono/PlayModeView/PlayModeView.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using UnityEditor.Modules; using UnityEditorInternal; @@ -22,15 +23,18 @@ internal static void RepaintAll() } [Serializable] - internal abstract class PlayModeView : EditorWindow, ISerializationCallbackReceiver + internal abstract class PlayModeView : EditorWindow { static List s_PlayModeViews = new List(); static PlayModeView s_LastFocused; static PlayModeView s_RenderingView; - Dictionary m_SerializedViews = new Dictionary(); - [SerializeField] private List m_SerializedViewsNames = new List(); - [SerializeField] private List m_SerializedViewsValues = new List(); + private readonly string m_ViewsCache = Path.GetFullPath(Directory.GetCurrentDirectory() + "/Library/PlayModeViewStates/"); + + [SerializeField] private List m_SerializedViewNames = new List(); + [SerializeField] private List m_SerializedViewValues = new List(); + [SerializeField] private List m_SerializedCustomFieldsNames = new List(); + [SerializeField] private List m_SerializedCustomFieldsValues = new List(); [SerializeField] string m_PlayModeViewName; [SerializeField] bool m_ShowGizmos; [SerializeField] int m_TargetDisplay; @@ -164,7 +168,8 @@ protected RenderTexture RenderView(Vector2 mousePosition, bool clearTexture) currentTargetDisplay = targetDisplay; } - ConfigureTargetTexture((int)targetSize.x, (int)targetSize.y, clearTexture, playModeViewName); + bool hdr = (m_Parent != null && m_Parent.actualView == this && m_Parent.hdrActive); + ConfigureTargetTexture((int)targetSize.x, (int)targetSize.y, clearTexture, playModeViewName, hdr); if (Event.current == null || Event.current.type != EventType.Repaint) return m_TargetTexture; @@ -173,7 +178,8 @@ protected RenderTexture RenderView(Vector2 mousePosition, bool clearTexture) GUIUtility.s_EditorScreenPointOffset = Vector2.zero; SavedGUIState oldState = SavedGUIState.Create(); - EditorGUIUtility.RenderPlayModeViewCamerasInternal(m_TargetTexture, currentTargetDisplay, mousePosition, showGizmos, renderIMGUI); + if (m_TargetTexture.IsCreated()) + EditorGUIUtility.RenderPlayModeViewCamerasInternal(m_TargetTexture, currentTargetDisplay, mousePosition, showGizmos, renderIMGUI); oldState.ApplyAndForget(); GUIUtility.s_EditorScreenPointOffset = oldOffset; @@ -204,7 +210,14 @@ protected virtual void DeserializeView(string serializedView) private void SetSerializedViews(Dictionary serializedViews) { - m_SerializedViews = serializedViews; + m_SerializedViewNames = serializedViews.Keys.ToList(); + m_SerializedViewValues = serializedViews.Values.ToList(); + } + + private void SetSerializedCustomFields(Dictionary serializedCustomFields) + { + m_SerializedCustomFieldsNames = serializedCustomFields.Keys.ToList(); + m_SerializedCustomFieldsValues = serializedCustomFields.Values.ToList(); } private string GetTypeName() @@ -212,28 +225,65 @@ private string GetTypeName() return GetType().ToString(); } + private Dictionary ListsToDictionary(List keys, List values) + { + var dict = keys.Select((key, val) => new { key, val = values[val] }).ToDictionary(x => x.key, x => x.val); + return dict; + } + protected void SwapMainWindow(Type type) { if (type.BaseType != typeof(PlayModeView)) throw new ArgumentException("Type should derive from " + typeof(PlayModeView).Name); if (type.Name != GetType().Name) { - var serializedViews = new Dictionary(m_SerializedViews); - m_SerializedViews.Clear(); + var serializedViews = ListsToDictionary(m_SerializedViewNames, m_SerializedViewValues); + var serializedCustomFields = ListsToDictionary(m_SerializedCustomFieldsNames, m_SerializedCustomFieldsValues); + + // Clear serialized views so they wouldn't be serialized again + m_SerializedViewNames.Clear(); + m_SerializedViewValues.Clear(); + m_SerializedCustomFieldsNames.Clear(); + m_SerializedCustomFieldsValues.Clear(); + var serializedObject = SerializeView(); if (serializedObject != null) - serializedViews.Add(GetTypeName(), serializedObject); + serializedCustomFields.Add(GetTypeName(), serializedObject); + + var guid = GUID.Generate(); + var serializedViewPath = Path.GetFullPath(Path.Combine(m_ViewsCache, guid.ToString())); + if (!Directory.Exists(m_ViewsCache)) + Directory.CreateDirectory(m_ViewsCache); + + InternalEditorUtility.SaveToSerializedFileAndForget(new[] {this}, serializedViewPath, true); + serializedViews.Add(GetTypeName(), serializedViewPath); + + PlayModeView window = null; + if (serializedViews.ContainsKey(type.ToString())) + { + var path = serializedViews[type.ToString()]; + serializedViews.Remove(type.ToString()); + if (File.Exists(path)) + { + window = InternalEditorUtility.LoadSerializedFileAndForget(path)[0] as PlayModeView; + File.Delete(path); + } + } + + if (!window) + window = CreateInstance(type) as PlayModeView; - var window = CreateInstance(type) as PlayModeView; - window.autoRepaintOnSceneChange = true; - if (serializedViews.ContainsKey(window.GetTypeName())) + if (serializedCustomFields.ContainsKey(window.GetTypeName())) { - window.DeserializeView(serializedViews[window.GetTypeName()]); - serializedViews.Remove(window.GetTypeName()); + window.DeserializeView(serializedCustomFields[window.GetTypeName()]); + serializedCustomFields.Remove(window.GetTypeName()); } + window.autoRepaintOnSceneChange = true; + window.SetSerializedViews(serializedViews); + window.SetSerializedCustomFields(serializedCustomFields); var da = m_Parent as DockArea; if (da) @@ -256,19 +306,24 @@ private void ClearTargetTexture() } } - private void ConfigureTargetTexture(int width, int height, bool clearTexture, string name) + private void ConfigureTargetTexture(int width, int height, bool clearTexture, string name, bool hdr) { + // make sure we actually support R16G16B16A16_SFloat + GraphicsFormat format = (hdr && SystemInfo.IsFormatSupported(GraphicsFormat.R16G16B16A16_SFloat, FormatUsage.Render)) ? GraphicsFormat.R16G16B16A16_SFloat : SystemInfo.GetGraphicsFormat(DefaultFormat.LDR); + // Requires destroying the entire RT object and recreating it if // 1. color space is changed; // 2. using mipmap is changed. - if (m_TargetTexture && (m_CurrentColorSpace != QualitySettings.activeColorSpace || m_TargetTexture.useMipMap != m_UseMipMap)) + // 3. HDR backbuffer mode for the view has changed + + if (m_TargetTexture && (m_CurrentColorSpace != QualitySettings.activeColorSpace || m_TargetTexture.useMipMap != m_UseMipMap || m_TargetTexture.graphicsFormat != format)) { UnityEngine.Object.DestroyImmediate(m_TargetTexture); } if (!m_TargetTexture) { m_CurrentColorSpace = QualitySettings.activeColorSpace; - m_TargetTexture = new RenderTexture(0, 0, 24, SystemInfo.GetGraphicsFormat(DefaultFormat.LDR)); + m_TargetTexture = new RenderTexture(0, 0, 24, format); m_TargetTexture.name = name + " RT"; m_TargetTexture.filterMode = textureFilterMode; m_TargetTexture.hideFlags = textureHideFlags; @@ -384,26 +439,5 @@ internal static void RepaintAll() foreach (PlayModeView playModeView in s_PlayModeViews) playModeView.Repaint(); } - - public void OnBeforeSerialize() - { - m_SerializedViewsNames = new List(); - m_SerializedViewsValues = new List(); - - foreach (var serializedView in m_SerializedViews) - { - m_SerializedViewsNames.Add(serializedView.Key); - m_SerializedViewsValues.Add(serializedView.Value); - } - } - - public void OnAfterDeserialize() - { - m_SerializedViews = new Dictionary(); - for (int i = 0; i < m_SerializedViewsNames.Count; i++) - { - m_SerializedViews.Add(m_SerializedViewsNames[i], m_SerializedViewsValues[i]); - } - } } } diff --git a/Editor/Mono/PlayerSettingsPS4.bindings.cs b/Editor/Mono/PlayerSettingsPS4.bindings.cs index 46a5447fb5..deed701e07 100644 --- a/Editor/Mono/PlayerSettingsPS4.bindings.cs +++ b/Editor/Mono/PlayerSettingsPS4.bindings.cs @@ -203,6 +203,7 @@ public static string SdkOverride [NativeProperty("ps4UseAudio3dBackend", false, TargetType.Field)] extern public static bool useAudio3dBackend { get; set; } [NativeProperty("ps4Audio3dVirtualSpeakerCount", false, TargetType.Field)] extern public static int audio3dVirtualSpeakerCount { get; set; } [NativeProperty("ps4ScriptOptimizationLevel", false, TargetType.Field)] extern public static int scriptOptimizationLevel { get; set; } + [NativeProperty("ps4UseLowGarlicFragmentationMode", true, TargetType.Field)] extern public static bool useLowGarlicFragmentationMode { get; set; } [NativeProperty("ps4SocialScreenEnabled", false, TargetType.Field)] extern public static int socialScreenEnabled { get; set; } [NativeProperty("ps4attribUserManagement", false, TargetType.Field)] extern public static bool attribUserManagement { get; set; } [NativeProperty("ps4attribMoveSupport", false, TargetType.Field)] extern public static bool attribMoveSupport { get; set; } diff --git a/Editor/Mono/Prefabs/PrefabUtility.cs b/Editor/Mono/Prefabs/PrefabUtility.cs index 6586ffa035..ec27a414b6 100644 --- a/Editor/Mono/Prefabs/PrefabUtility.cs +++ b/Editor/Mono/Prefabs/PrefabUtility.cs @@ -641,10 +641,32 @@ static void ApplySingleProperty( return; } + SerializedProperty sourceProperty = prefabSourceSerializedObject.FindProperty(instanceProperty.propertyPath); + if (sourceProperty == null) + { + bool cancel; + var instanceArrayProperty = GetArrayPropertyIfGivenPropertyIsPartOfArrayElementInInstanceWhichDoesNotExistInAsset(instanceProperty, prefabSourceSerializedObject, InteractionMode.AutomatedAction, out cancel); + if (instanceArrayProperty != null) + { + prefabSourceSerializedObject.CopyFromSerializedProperty(instanceArrayProperty); + sourceProperty = prefabSourceSerializedObject.FindProperty(instanceProperty.propertyPath); + if (sourceProperty == null) + { + Debug.LogError($"ApplySingleProperty full array copy error: SerializedProperty could not be found for {instanceProperty.propertyPath}. Please report a bug."); + return; + } + } + else + { + Debug.LogError($"ApplySingleProperty copy state error: SerializedProperty could not be found for {instanceProperty.propertyPath}. Please report a bug."); + return; + } + } + + // Copy overridden property value to asset prefabSourceSerializedObject.CopyFromSerializedProperty(instanceProperty); // Abort if property has reference to object in scene. - SerializedProperty sourceProperty = prefabSourceSerializedObject.FindProperty(instanceProperty.propertyPath); if (sourceProperty.propertyType == SerializedPropertyType.ObjectReference) { MapObjectReferencePropertyToSourceIfApplicable(sourceProperty, assetPath); @@ -705,7 +727,7 @@ static void ApplySingleProperty( outerPrefabProp.prefabOverride = false; } if (outerPrefabProp == null) - Debug.LogError($"ApplySingleProperty error: SerializedProperty could not be found for {instanceProperty.propertyPath}. Please report a bug."); + Debug.LogError($"ApplySingleProperty clear overrides error: SerializedProperty could not be found for {instanceProperty.propertyPath}. Please report a bug."); outerPrefabObject = PrefabUtility.GetCorrespondingObjectFromSource(outerPrefabObject); sourceIndex++; diff --git a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs index d0e9911cd2..8783bd72bd 100644 --- a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs +++ b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs @@ -82,6 +82,7 @@ internal class GeneralProperties EditorGUIUtility.TrTextContent("Monitor Refresh Rate", "The editor will wait up to the monitor refresh rate in milliseconds (i.e. ~16 ms)."), EditorGUIUtility.TrTextContent("Custom", "Specify how many milliseconds at most the application will be idle per frame."), }; + public static readonly GUIContent progressDialogDelay = EditorGUIUtility.TrTextContent("Busy Progress Delay", "Delay in seconds before 'Unity is busy' progress bar shows up."); } internal class ExternalProperties @@ -185,6 +186,7 @@ private struct GICacheSettings private bool m_EnableCodeCoverage = false; private bool m_EnableCodeCoverageChangedInThisSession = false; private bool m_Create3DObjectsAtOrigin = false; + private float m_ProgressDialogDelay = 3.0f; private string[] m_ScriptApps; private string[] m_ScriptAppsEditions; @@ -597,6 +599,17 @@ private void ShowGeneral(string searchContext) m_Create3DObjectsAtOrigin = EditorGUILayout.Toggle(GeneralProperties.createObjectsAtWorldOrigin, m_Create3DObjectsAtOrigin); + if (Application.platform == RuntimePlatform.WindowsEditor) + { + var progressDialogDelay = EditorGUILayout.DelayedFloatField(GeneralProperties.progressDialogDelay, m_ProgressDialogDelay); + progressDialogDelay = Mathf.Clamp(progressDialogDelay, 0.1f, 1000.0f); + if (progressDialogDelay != m_ProgressDialogDelay) + { + EditorUtility.BusyProgressDialogDelayChanged(progressDialogDelay); + m_ProgressDialogDelay = progressDialogDelay; + } + } + ApplyChangesToPrefs(); if (oldAlphaNumeric != m_AllowAlphaNumericHierarchy) @@ -1045,6 +1058,7 @@ private void WritePreferences() } EditorPrefs.SetBool("Create3DObject.PlaceAtWorldOrigin", m_Create3DObjectsAtOrigin); + EditorPrefs.SetFloat("EditorBusyProgressDialogDelay", m_ProgressDialogDelay); EditorPrefs.SetString("GpuDeviceName", m_GpuDevice); EditorPrefs.SetBool("GICacheEnableCustomPath", m_GICacheSettings.m_EnableCustomPath); @@ -1153,6 +1167,7 @@ private void ReadPreferences() m_AllowAlphaNumericHierarchy = EditorPrefs.GetBool("AllowAlphaNumericHierarchy", false); m_EnableCodeCoverage = EditorPrefs.GetBool("CodeCoverageEnabled", false); m_Create3DObjectsAtOrigin = EditorPrefs.GetBool("Create3DObject.PlaceAtWorldOrigin", false); + m_ProgressDialogDelay = EditorPrefs.GetFloat("EditorBusyProgressDialogDelay", 3.0f); m_CompressAssetsOnImport = Unsupported.GetApplicationSettingCompressAssetsOnImport(); m_GpuDevice = EditorPrefs.GetString("GpuDeviceName"); diff --git a/Editor/Mono/Progress/ProgressWindow.cs b/Editor/Mono/Progress/ProgressWindow.cs index 03a50273b6..07156f2e6a 100644 --- a/Editor/Mono/Progress/ProgressWindow.cs +++ b/Editor/Mono/Progress/ProgressWindow.cs @@ -43,6 +43,17 @@ public static void ShowDetails() ShowDetails(false); } + internal static bool canHideDetails => m_Window && !m_Window.docked; + + internal static void HideDetails() + { + if (canHideDetails) + { + m_Window.Close(); + m_Window = null; + } + } + internal static void ShowDetails(bool shouldReposition) { if (m_Window && m_Window.docked) @@ -154,12 +165,15 @@ private void OnDisable() private void CheckUnresponsive() { + EditorApplication.delayCall -= CheckUnresponsive; + foreach (var progressElement in m_Elements) { progressElement.CheckUnresponsive(); } - EditorApplication.delayCall += CheckUnresponsive; + if (Progress.running) + EditorApplication.delayCall += CheckUnresponsive; } private void OperationWasAdded(Progress.Item[] ops) @@ -169,6 +183,7 @@ private void OperationWasAdded(Progress.Item[] ops) UpdateNbTasks(); UpdateStatusHeaders(); UpdateStatusFilter(el); + CheckUnresponsive(); } private void OperationWasRemoved(Progress.Item[] ops) @@ -198,6 +213,7 @@ private void OperationWasRemoved(Progress.Item[] ops) { UpdateStatusHeaders(); UpdateNbTasks(); + CheckUnresponsive(); }; } @@ -242,6 +258,7 @@ private void OperationWasUpdated(Progress.Item[] ops) UpdateStatusFilter(m_Elements[parentElementIndex]); } m_DismissAllBtn.SetEnabled(m_Elements.Any(el => !el.dataSource.running)); + CheckUnresponsive(); } private int FindIndexFirstSucceededOrCanceledElement(List elements) diff --git a/Editor/Mono/ProjectBrowser.cs b/Editor/Mono/ProjectBrowser.cs index deb82494e5..c899ba7b9a 100644 --- a/Editor/Mono/ProjectBrowser.cs +++ b/Editor/Mono/ProjectBrowser.cs @@ -1126,6 +1126,8 @@ void ListAreaItemSelectedCallback(bool doubleClicked) m_SearchFilter.searchArea = m_LastLocalAssetsSearchArea; // local asset was selected m_InternalSelectionChange = true; } + else if (AssetStoreAssetSelection.Count > 0) + Selection.activeObject = AssetStoreAssetInspector.Instance; m_FocusSearchField = false; @@ -1213,6 +1215,14 @@ void OnSelectionChange() } m_InternalSelectionChange = false; + + // Clear asset store asset selection + if (Selection.activeObject != null && Selection.activeObject.GetType() != typeof(AssetStoreAssetInspector)) + { + m_ListArea.selectedAssetStoreAsset = false; + AssetStoreAssetSelection.Clear(); + } + RefreshSelectedPath(); Repaint(); } @@ -2003,7 +2013,12 @@ void HandleContextClickInListArea(Rect listRect) if (listRect.Contains(evt.mousePosition)) { GUIUtility.hotControl = 0; - EditorUtility.DisplayPopupMenu(new Rect(evt.mousePosition.x, evt.mousePosition.y, 0, 0), "Assets/", null); + // Context click in list area (can be an asset store asset or a local asset) + if (AssetStoreAssetSelection.GetFirstAsset() != null) + AssetStoreItemContextMenu.Show(); + else + EditorUtility.DisplayPopupMenu(new Rect(evt.mousePosition.x, evt.mousePosition.y, 0, 0), "Assets/", null); + evt.Use(); } break; @@ -2999,5 +3014,33 @@ private void SelectSubFolder() m_Caller.ShowFolderContents(folderInstanceID, false); } } + + internal class AssetStoreItemContextMenu + { + static internal void Show() + { + GenericMenu menu = new GenericMenu(); + + GUIContent assetStoreWindow = EditorGUIUtility.TrTextContent("Show in Asset Store window"); + AssetStoreAsset activeAsset = AssetStoreAssetSelection.GetFirstAsset(); + if (activeAsset != null && activeAsset.id != 0) + menu.AddItem(assetStoreWindow, false, new AssetStoreItemContextMenu().OpenAssetStoreWindow); + else + menu.AddDisabledItem(assetStoreWindow); + + menu.ShowAsContext(); + } + + private void OpenAssetStoreWindow() + { + AssetStoreAsset activeAsset = AssetStoreAssetSelection.GetFirstAsset(); + if (activeAsset != null) + AssetStoreAssetInspector.OpenItemInAssetStore(activeAsset); + } + + private AssetStoreItemContextMenu() + { + } + } } } diff --git a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs index a23f1c5880..a3f4b8f035 100644 --- a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs +++ b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs @@ -833,8 +833,6 @@ internal static bool DeleteAssets(List instanceIDs, bool askIfSure) internal static IEnumerable DuplicateAssets(IEnumerable assets) { - AssetDatabase.Refresh(); - var copiedPaths = new List(); Object firstDuplicatedObjectToFail = null; @@ -896,8 +894,6 @@ internal static IEnumerable DuplicateAssets(IEnumerable assets) Debug.LogError(errString, firstDuplicatedObjectToFail); } - AssetDatabase.Refresh(); - return copiedPaths.Select(AssetDatabase.LoadMainAssetAtPath); } diff --git a/Editor/Mono/QuickSearch.cs b/Editor/Mono/QuickSearch.cs index fe58faf0dd..6de9ead688 100644 --- a/Editor/Mono/QuickSearch.cs +++ b/Editor/Mono/QuickSearch.cs @@ -36,7 +36,7 @@ private static void OpenQuickSearch() var quickSearchPackage = searchLatestRequest.Result.FirstOrDefault(p => p.name == k_QuickSearchPackageId); if (quickSearchPackage != null && EditorUtility.DisplayDialog( $"Shoot! {quickSearchPackage.displayName} is not installed yet!", - $"Do you want to install {quickSearchPackage.displayName} ({quickSearchPackage.versions.latest}) and be more productive?" + + $"Do you want to install {quickSearchPackage.displayName} ({quickSearchPackage.versions.verified}) and be more productive?" + $"\r\n\r\nPackage Description: {quickSearchPackage.description}", "Yes", "No")) { // Install a token that will be read by the quick search package once @@ -46,7 +46,7 @@ private static void OpenQuickSearch() // Add quick search package entry. the added package will // be compiled and a domain reload will occur. - var packageIdToInstall = $"{quickSearchPackage.name}@{quickSearchPackage.versions.latest}"; + var packageIdToInstall = $"{quickSearchPackage.name}@{quickSearchPackage.versions.verified}"; var addQuickSearchRequest = PackageManager.Client.Add(packageIdToInstall); if (!WaitForRequest(addQuickSearchRequest, $"Installing {quickSearchPackage.displayName}...")) Debug.LogError($"Failed to install {packageIdToInstall}"); diff --git a/Editor/Mono/SceneHierarchy.cs b/Editor/Mono/SceneHierarchy.cs index ae4378113c..bb7efc9649 100644 --- a/Editor/Mono/SceneHierarchy.cs +++ b/Editor/Mono/SceneHierarchy.cs @@ -1177,8 +1177,11 @@ void CreateGameObjectContextClick(GenericMenu menu, int contextClickedItemID) SceneHierarchyHooks.AddCustomGameObjectContextMenuItems(menu, contextClickedItemID == 0 ? null : (GameObject)EditorUtility.InstanceIDToObject(contextClickedItemID)); - menu.AddSeparator(""); - menu.AddItem(new GUIContent("Properties..."), false, () => PropertyEditor.OpenPropertyEditorOnSelection()); + if (selectedGameObjects.Length > 0) + { + menu.AddSeparator(""); + menu.AddItem(new GUIContent("Properties..."), false, () => PropertyEditor.OpenPropertyEditorOnSelection()); + } menu.ShowAsContext(); } diff --git a/Editor/Mono/SceneHierarchyWindow.cs b/Editor/Mono/SceneHierarchyWindow.cs index 66c28ecef0..e01f3474d3 100644 --- a/Editor/Mono/SceneHierarchyWindow.cs +++ b/Editor/Mono/SceneHierarchyWindow.cs @@ -61,6 +61,7 @@ public override void OnEnable() m_StageHandling.OnEnable(); titleContent = GetLocalizedTitleContent(); + wantsLessLayoutEvents = true; } public override void OnDisable() diff --git a/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs b/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs index 517ec5ec24..911a970a4b 100644 --- a/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs +++ b/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs @@ -74,6 +74,7 @@ public enum Mode string m_PrefabAssetPath; GameObject m_OpenedFromInstanceRoot; GameObject m_OpenedFromInstanceObject; + ulong m_FileIdForOpenedFromInstanceObject; Stage m_ContextStage = null; Mode m_Mode; int m_InitialSceneDirtyID; @@ -90,6 +91,9 @@ public enum Mode Transform m_LastRootTransform; const float kDurationBeforeShowingSavingBadge = 1.0f; static ExposablePopupMenu s_ContextRenderModeSelector; + Hash128 m_LastPrefabSourceFileHash; + bool m_NeedsReloadingWhenReturningToStage; + bool m_IsAssetMissing; [System.Serializable] struct PatchedProperty @@ -116,23 +120,63 @@ private PrefabStage() { } - void Init(string prefabAssetPath, GameObject openedFromInstanceObject, PrefabStage.Mode prefabStageMode, Stage contextStage) + void SetOpenedFromInstanceObject(GameObject go) { - m_PrefabAssetPath = prefabAssetPath; - CachePrefabFolderInfo(); + if (go != null) + { + if (!PrefabUtility.IsPartOfPrefabInstance(go)) + throw new ArgumentException("GameObject must be part of a Prefab instance, or null.", nameof(go)); - m_OpenedFromInstanceObject = openedFromInstanceObject; - if (openedFromInstanceObject != null) + m_OpenedFromInstanceObject = go; + m_OpenedFromInstanceRoot = PrefabUtility.GetNearestPrefabInstanceRoot(go); + m_FileIdForOpenedFromInstanceObject = Unsupported.GetOrGenerateFileIDHint(go); + } + else { - if (!PrefabUtility.IsPartOfPrefabInstance(openedFromInstanceObject)) - throw new ArgumentException("GameObject must be part of a Prefab instance, or null.", nameof(openedFromInstanceObject)); - m_OpenedFromInstanceRoot = PrefabUtility.GetNearestPrefabInstanceRoot(openedFromInstanceObject); + m_OpenedFromInstanceObject = null; + m_OpenedFromInstanceRoot = null; + m_FileIdForOpenedFromInstanceObject = 0; } + } + + void Init(string prefabAssetPath, GameObject openedFromInstanceGameObject, PrefabStage.Mode prefabStageMode, Stage contextStage) + { + m_PrefabAssetPath = prefabAssetPath; + CachePrefabFolderInfo(); + SetOpenedFromInstanceObject(openedFromInstanceGameObject); + if (prefabStageMode == PrefabStage.Mode.InContext) m_ContextStage = contextStage; m_Mode = prefabStageMode; } + void ReconstructDataIfNeeded() + { + bool needsReconstruction = m_OpenedFromInstanceObject == null && m_FileIdForOpenedFromInstanceObject != 0; + if (!needsReconstruction) + return; + + // The previous PrefabStage can have been reloaded which means we need to update our reference to m_OpenedFromInstanceObject + // to the newly loaded GameObject (the old GameObject was deleted as part of reloading the PrefabStage). + var history = StageNavigationManager.instance.stageHistory; + int index = history.IndexOf(this); + int previousIndex = index - 1; + var previousStage = history[previousIndex]; + var prevPrefabStage = previousStage as PrefabStage; + if (prevPrefabStage) + { + var go = PrefabStageUtility.FindFirstGameObjectThatMatchesFileID(prevPrefabStage.prefabContentsRoot.transform, m_FileIdForOpenedFromInstanceObject, true); + if (go != null) + { + SetOpenedFromInstanceObject(go); + } + else + { + Debug.LogError("Could not find GameObject with fileID " + m_FileIdForOpenedFromInstanceObject + " in PrefabStage for: " + prevPrefabStage.assetPath); + } + } + } + internal bool analyticsDidUserModify { get { return m_AnalyticsDidUserModify; } } internal bool analyticsDidUserSave { get { return m_AnalyticsDidUserSave; } } @@ -195,6 +239,11 @@ public Mode mode get { return m_Mode; } } + bool isCurrentStage + { + get { return StageUtility.GetCurrentStage() == this; } + } + public override ulong GetCombinedSceneCullingMaskForCamera() { if (m_Mode == Mode.InIsolation) @@ -296,7 +345,7 @@ internal bool autoSave internal bool temporarilyDisableAutoSave { - get { return m_TemporarilyDisableAutoSave; } + get { return m_TemporarilyDisableAutoSave || isAssetMissing; } } internal override bool isValid @@ -306,7 +355,7 @@ internal override bool isValid internal override bool isAssetMissing { - get { return !File.Exists(m_PrefabAssetPath); } + get { return m_IsAssetMissing; } } void OnPrefabInstanceUpdated(GameObject instance) @@ -335,6 +384,8 @@ void SetPrefabInstanceHiddenForInContextEditing(bool hide) bool LoadStage() { + ReconstructDataIfNeeded(); + string prefabPath = m_PrefabAssetPath; GameObject openedFromInstanceObject = m_OpenedFromInstanceObject; Mode prefabStageMode = m_Mode; @@ -434,6 +485,12 @@ bool LoadStage() // Returns true if opened successfully protected internal override bool OnOpenStage() { + if (!isCurrentStage) + { + Debug.LogError("Only opening the current PrefabStage is supported. Please report a bug"); + return false; + } + if (LoadStage()) { if (mode == Mode.InContext) @@ -448,6 +505,7 @@ protected internal override bool OnOpenStage() // Note: The user can have reparented and created new GameObjects in the environment scene during this callback. EnsureParentOfPrefabRootIsUnpacked(); UpdateEnvironmentHideFlags(); + UpdateLastPrefabSourceFileHashIfNeeded(); var sceneHierarchyWindows = SceneHierarchyWindow.GetAllSceneHierarchyWindows(); foreach (SceneHierarchyWindow sceneHierarchyWindow in sceneHierarchyWindows) @@ -466,6 +524,25 @@ protected override void OnCloseStage() Cleanup(); } + protected internal override void OnReturnToStage() + { + if (m_NeedsReloadingWhenReturningToStage) + { + m_NeedsReloadingWhenReturningToStage = false; + + if (m_Mode == Mode.InContext && m_OpenedFromInstanceObject == null) + { + // By clearing the contents root this stage becomes invalid which + // will be handled the StageNavigationManager by returning to the + // main stage + m_PrefabContentsRoot = null; + return; + } + + ReloadStage(); + } + } + bool HasPatchedPropertyModificationsFor(UnityEngine.Object obj, string partialPropertyName) { if (m_PatchedProperties == null) @@ -493,6 +570,13 @@ internal bool ContainsTransformPrefabPropertyPatchingFor(GameObject[] gameObject void RecordPatchedPropertiesForContent() { m_PatchedProperties = new List(); + + if (openedFromInstanceRoot == null) + return; + + if (PrefabUtility.GetPrefabInstanceStatus(openedFromInstanceRoot) != PrefabInstanceStatus.Connected) + return; + Dictionary contentObjectsFromFileID = new Dictionary(); Dictionary instanceTransformsFromFileID = new Dictionary(); @@ -665,6 +749,9 @@ void RecordPatchedPropertiesForContent() void ApplyPatchedPropertiesToContent() { + if (m_PatchedProperties.Count == 0) + return; + for (int i = m_PatchedProperties.Count - 1; i >= 0; i--) { PropertyModification mod = m_PatchedProperties[i].modification; @@ -723,6 +810,12 @@ void ReloadStage() if (SceneHierarchy.s_DebugPrefabStage) Debug.Log("RELOADING Prefab at " + m_PrefabAssetPath); + if (!isCurrentStage) + { + Debug.LogError("Only reloading the current PrefabStage is supported. Please report a bug"); + return; + } + var sceneHierarchyWindows = SceneHierarchyWindow.GetAllSceneHierarchyWindows(); foreach (SceneHierarchyWindow sceneHierarchyWindow in sceneHierarchyWindows) SaveHierarchyState(sceneHierarchyWindow); @@ -1029,6 +1122,12 @@ void HandlePrefabChangedOnDisk() { m_PrefabWasChangedOnDisk = false; + if (!isCurrentStage) + { + m_NeedsReloadingWhenReturningToStage = true; + return; + } + if (!File.Exists(m_PrefabAssetPath)) return; @@ -1381,11 +1480,17 @@ internal bool AskUserToSaveDirtySceneBeforeDestroyingScene() switch (dialogResult) { case 0: - return Save(); // save changes and continue if possible + return Save(); // save changes and continue current operation + case 1: - return true; // discard changes and continue + // The user have accepted to discard changes + if (hasUnsavedChanges && !m_IsAssetMissing) + ReloadStage(); + return true; // continue current operation + case 2: return false; // cancel and discontinue current operation + default: throw new InvalidOperationException("Unhandled dialog result " + dialogResult); } @@ -1406,6 +1511,18 @@ internal void OnSavingPrefab(GameObject gameObject, string path) } } + bool UpdateLastPrefabSourceFileHashIfNeeded() + { + var guid = AssetDatabase.AssetPathToGUID(m_PrefabAssetPath); + var prefabSourceFileHash = AssetDatabase.GetSourceAssetFileHash(guid); + if (m_LastPrefabSourceFileHash != prefabSourceFileHash) + { + m_LastPrefabSourceFileHash = prefabSourceFileHash; + return true; + } + return false; + } + internal void OnAssetsChangedOnHDD(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { if (SceneHierarchy.s_DebugPrefabStage) @@ -1421,13 +1538,25 @@ internal void OnAssetsChangedOnHDD(string[] importedAssets, string[] deletedAsse } } + for (int i = 0; i < deletedAssets.Length; ++i) + { + if (deletedAssets[i] == m_PrefabAssetPath) + { + m_IsAssetMissing = true; + break; + } + } + // Detect if our Prefab was modified on HDD outside Prefab Mode (in that case we should ask the user if he wants to reload it) for (int i = 0; i < importedAssets.Length; ++i) { if (importedAssets[i] == m_PrefabAssetPath) { - if (!m_IgnoreNextAssetImportedEventForCurrentPrefab) + m_IsAssetMissing = false; + if (UpdateLastPrefabSourceFileHashIfNeeded() && !m_IgnoreNextAssetImportedEventForCurrentPrefab) + { m_PrefabWasChangedOnDisk = true; + } // Reset the ignore flag when we finally have imported the saved prefab (We set this flag when saving the Prefab from Prefab Mode) // Note we can get multiple OnAssetsChangedOnHDD events before the Prefab imported event if e.g folders of the Prefab path needs to be reimported first. @@ -1619,14 +1748,13 @@ void AutoSaveButtons(SceneView sceneView) if (!autoSave) { - using (new EditorGUI.DisabledScope(!openForEdit || !hasUnsavedChanges)) + using (new EditorGUI.DisabledScope((!openForEdit || !hasUnsavedChanges) && !isAssetMissing)) { if (GUILayout.Button(Styles.saveButtonContent, Styles.button)) Save(); } } - using (new EditorGUI.DisabledScope(temporarilyDisableAutoSave)) { bool autoSaveForScene = autoSave; EditorGUI.BeginChangeCheck(); diff --git a/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStageUtility.cs b/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStageUtility.cs index 7043977e18..1eac50faff 100644 --- a/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStageUtility.cs +++ b/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStageUtility.cs @@ -140,8 +140,9 @@ internal static PrefabStage OpenPrefabMode(string prefabAssetPath, GameObject op prefabStage = PrefabStage.CreatePrefabStage(prefabAssetPath, openedFromInstance, prefabStageMode, contextStage); if (StageNavigationManager.instance.SwitchToStage(prefabStage, setAsFirstItemAfterMainStage, changeTypeAnalytics)) { - // If selection did not change by switching stage (by us or user) then handle automatic selection in new prefab mode - if (Selection.activeGameObject == previousSelection) + // If selection did not change by switching stage by us or user (or if current selection is not part of + // the opened prefab stage) then handle automatic selection in new prefab mode. + if (Selection.activeGameObject == previousSelection || !prefabStage.IsPartOfPrefabContents(Selection.activeGameObject)) { HandleSelectionWhenSwithingToNewPrefabMode(GetCurrentPrefabStage().prefabContentsRoot, previousFileID); } @@ -304,7 +305,7 @@ static UInt64 GetPrefabOrVariantFileID(GameObject gameObject) return Unsupported.GetFileIDHint(gameObject); } - static GameObject FindFirstGameObjectThatMatchesFileID(Transform searchRoot, UInt64 fileID, bool generate) + internal static GameObject FindFirstGameObjectThatMatchesFileID(Transform searchRoot, UInt64 fileID, bool generate) { GameObject result = null; var transformVisitor = new TransformVisitor(); diff --git a/Editor/Mono/SceneManagement/StageManager/Stage.cs b/Editor/Mono/SceneManagement/StageManager/Stage.cs index bc1a29ba63..3099396bd7 100644 --- a/Editor/Mono/SceneManagement/StageManager/Stage.cs +++ b/Editor/Mono/SceneManagement/StageManager/Stage.cs @@ -26,6 +26,9 @@ public abstract class Stage : ScriptableObject // Only called if OnOpenStage was called. protected abstract void OnCloseStage(); + // Called when returning to a previous open stage (e.g by clicking a non-current breadcrumb) + protected internal virtual void OnReturnToStage() {} + internal bool opened { get; set; } internal virtual bool isValid { get { return true; } } diff --git a/Editor/Mono/SceneManagement/StageManager/StageNavigationManager.cs b/Editor/Mono/SceneManagement/StageManager/StageNavigationManager.cs index c77b46869d..a7ff7be484 100644 --- a/Editor/Mono/SceneManagement/StageManager/StageNavigationManager.cs +++ b/Editor/Mono/SceneManagement/StageManager/StageNavigationManager.cs @@ -190,13 +190,6 @@ internal bool SwitchToStage(Stage stage, bool setAsFirstItemAfterMainStage, Anal return false; } - if (stage.isAssetMissing) - { - Debug.LogError($"Cannot switch to new stage. Asset does not exist so stage cannot be reconstructed: {stage.assetPath}"); - DestroyImmediate(stage); - return false; - } - bool setPreviousSelection = stage.opened; StopAnimationPlaybackAndPreviewing(); @@ -255,6 +248,7 @@ internal bool SwitchToStage(Stage stage, bool setAsFirstItemAfterMainStage, Anal } else { + stage.OnReturnToStage(); success = stage.isValid; } diff --git a/Editor/Mono/SceneModeWindows/LightingWindowLightingTab.cs b/Editor/Mono/SceneModeWindows/LightingWindowLightingTab.cs index c459ffa5d6..7184175aed 100644 --- a/Editor/Mono/SceneModeWindows/LightingWindowLightingTab.cs +++ b/Editor/Mono/SceneModeWindows/LightingWindowLightingTab.cs @@ -111,8 +111,11 @@ public void OnGUI() if (GUILayout.Button(Styles.newLightingSettings, GUILayout.Width(170))) { - Lightmapping.lightingSettingsInternal = new LightingSettings(); - Lightmapping.lightingSettingsInternal.CreateAsset(); + var ls = new LightingSettings(); + ls.name = "New Lighting Settings"; + Undo.RecordObject(m_LightmapSettings.targetObject, "New Lighting Settings"); + Lightmapping.lightingSettingsInternal = ls; + ProjectWindowUtil.CreateAsset(ls, (ls.name + ".lighting")); } GUILayout.EndHorizontal(); diff --git a/Editor/Mono/SceneView/SceneView.cs b/Editor/Mono/SceneView/SceneView.cs index 28d9baa9f4..9306fd75d9 100644 --- a/Editor/Mono/SceneView/SceneView.cs +++ b/Editor/Mono/SceneView/SceneView.cs @@ -305,7 +305,7 @@ public class SceneViewState internal bool particleSystemsEnabled => fxEnabled && showParticleSystems; internal bool visualEffectGraphsEnabled => fxEnabled && showVisualEffectGraphs; - [SerializeField] bool m_FxEnabled; + [SerializeField] bool m_FxEnabled = true; public SceneViewState() { @@ -313,6 +313,7 @@ public SceneViewState() public SceneViewState(SceneViewState other) { + fxEnabled = other.fxEnabled; showFog = other.showFog; showMaterialUpdate = other.showMaterialUpdate; showSkybox = other.showSkybox; @@ -1076,6 +1077,7 @@ public override void OnEnable() sceneViewGrids.gridVisibilityChanged += GridOnGridVisibilityChanged; wantsMouseMove = true; + wantsLessLayoutEvents = true; wantsMouseEnterLeaveWindow = true; s_SceneViews.Add(this); @@ -2650,15 +2652,15 @@ protected virtual void OnGUI() Tools.InvalidateHandlePosition(); // Some cases that should invalidate the cached position are not handled correctly yet so we refresh it once per frame } + sceneViewGrids.UpdateGridColor(); + Color origColor = GUI.color; Rect origCameraRect = m_Camera.rect; Rect windowSpaceCameraRect = cameraRect; HandleClickAndDragToFocus(); - if (evt.type == EventType.Layout) - m_ShowSceneViewWindows = (lastActiveSceneView == this); - + m_ShowSceneViewWindows = (lastActiveSceneView == this); m_SceneViewOverlay.Begin(); bool oldFog; @@ -2724,7 +2726,9 @@ protected virtual void OnGUI() //Ensure that the target texture is clamped [0-1] //This is needed because otherwise gizmo rendering gets all //messed up (think HDR target with value of 50 + alpha blend gizmo... gonna be white!) - if (!UseSceneFiltering() && evt.type == EventType.Repaint && GraphicsFormatUtility.IsIEEE754Format(m_SceneTargetTexture.graphicsFormat)) + + bool hdrDisplayActive = (m_Parent != null && m_Parent.actualView == this && m_Parent.hdrActive); + if (!UseSceneFiltering() && evt.type == EventType.Repaint && GraphicsFormatUtility.IsIEEE754Format(m_SceneTargetTexture.graphicsFormat) && !hdrDisplayActive) { var currentDepthBuffer = Graphics.activeDepthBuffer; var rtDesc = m_SceneTargetTexture.descriptor; diff --git a/Editor/Mono/SceneView/SceneViewGrid.cs b/Editor/Mono/SceneView/SceneViewGrid.cs index 77ea855fb2..dd5c9ab08b 100644 --- a/Editor/Mono/SceneView/SceneViewGrid.cs +++ b/Editor/Mono/SceneView/SceneViewGrid.cs @@ -202,9 +202,14 @@ internal Grid activeGrid } } - internal void OnEnable(SceneView view) + internal void UpdateGridColor() { xGrid.color = yGrid.color = zGrid.color = kViewGridColor; + } + + internal void OnEnable(SceneView view) + { + UpdateGridColor(); GridSettings.sizeChanged += GridSizeChanged; diff --git a/Editor/Mono/Scripting/APIUpdater/APIUpdaterAssemblyHelper.cs b/Editor/Mono/Scripting/APIUpdater/APIUpdaterAssemblyHelper.cs index 4a72c252f2..8ba0335f65 100644 --- a/Editor/Mono/Scripting/APIUpdater/APIUpdaterAssemblyHelper.cs +++ b/Editor/Mono/Scripting/APIUpdater/APIUpdaterAssemblyHelper.cs @@ -111,7 +111,6 @@ private static string ResolveAssemblyPath(string assemblyPath) private static string AssemblySearchPathArgument(IEnumerable configurationSourceDirectories = null) { var searchPath = Path.Combine(MonoInstallationFinder.GetFrameWorksFolder(), "Managed") + "," - + "+" + Path.Combine(EditorApplication.applicationContentsPath, "UnityExtensions/Unity") + "," + "+" + Application.dataPath; if (configurationSourceDirectories != null) diff --git a/Editor/Mono/Scripting/APIUpdater/AssemblyDependencyGraph.cs b/Editor/Mono/Scripting/APIUpdater/AssemblyDependencyGraph.cs index 1196666dfb..008334ff5a 100644 --- a/Editor/Mono/Scripting/APIUpdater/AssemblyDependencyGraph.cs +++ b/Editor/Mono/Scripting/APIUpdater/AssemblyDependencyGraph.cs @@ -133,13 +133,17 @@ public IEnumerable SortedDependents() var array = m_Graph.ToArray(); - CheckForCycles(array); + m_Processed = new HashSet(); + LogCycles(array, m_Processed); + + m_Processed.Clear(); bool exchangeElementsInLastPass; + var arrayLength = array.Length - 1; do { exchangeElementsInLastPass = false; - for (int i = 0; i < array.Length - 1; i++) + for (int i = 0; i < arrayLength; i++) { if (CompareElements(array[i], array[i + 1]) > 0) { @@ -150,6 +154,8 @@ public IEnumerable SortedDependents() exchangeElementsInLastPass = true; } } + + arrayLength--; } while (exchangeElementsInLastPass); @@ -163,46 +169,56 @@ public IEnumerable SortedDependents() */ private int CompareElements(DependencyEntry lhs, DependencyEntry rhs) { - var rshDependsOnLhs = HasDirectOrIndirectDependency(lhs, rhs); - if (rshDependsOnLhs) + var lhsDependsOnRhs = HasDirectOrIndirectDependency(lhs, rhs); + if (lhsDependsOnRhs) return 1; - var lhsDependsOnRhs = HasDirectOrIndirectDependency(rhs, lhs); - if (lhsDependsOnRhs) + var rshDependsOnLhs = HasDirectOrIndirectDependency(rhs, lhs); + if (rshDependsOnLhs) return -1; return 0; } - private static bool HasDirectOrIndirectDependency(DependencyEntry lhs, DependencyEntry rhs) + private bool HasDirectOrIndirectDependency(DependencyEntry lhs, DependencyEntry rhs) { var lhsDependsOnRhs = lhs.m_Dependencies.Contains(rhs); if (lhsDependsOnRhs) return true; + m_Processed.Clear(); return HasDirectOrIndirectDependencyRecursive(rhs, lhs.m_Dependencies); } - private static bool HasDirectOrIndirectDependencyRecursive(DependencyEntry toBeLookedUp, IList dependencies) + bool HasDirectOrIndirectDependencyRecursive(DependencyEntry toBeLookedUp, IList dependencies) { foreach (var entry in dependencies) { if (entry == toBeLookedUp) return true; - if (HasDirectOrIndirectDependencyRecursive(toBeLookedUp, entry.m_Dependencies)) - return true; + if (m_Processed.Contains(entry.Name)) + { + // We've found a cycle in the assemblies (which has already been logged) + return false; + } + + m_Processed.Add(entry.Name); + try + { + if (HasDirectOrIndirectDependencyRecursive(toBeLookedUp, entry.m_Dependencies)) + return true; + } + finally + { + m_Processed.Remove(entry.Name); + } } return false; } - static void CheckForCycles(IEnumerable entries) - { - CheckForCycles(entries, new HashSet()); - } - - static void CheckForCycles(IEnumerable entries, HashSet seen) + static void LogCycles(IEnumerable entries, HashSet seen) { foreach (var entry in entries) { @@ -211,11 +227,13 @@ static void CheckForCycles(IEnumerable entries, HashSet if (seen.Contains(entry.Name)) { - throw new InvalidOperationException($"[APIUpdater] Cycle detected in assembly references: {string.Join("->", seen.Reverse().ToArray())}->{entry.Name}"); + Console.WriteLine($"[APIUpdater] Warning: Cycle detected in assembly references: {string.Join("->", seen.ToArray())}->{entry.Name}. This is not supported and AssemblyUpdater may not work as expected."); + continue; } seen.Add(entry.Name); - CheckForCycles(entry.Dependencies, seen); + + LogCycles(entry.Dependencies, seen); entry.Status |= AssemblyStatus.NoCyclesDetected; seen.Remove(entry.Name); @@ -245,10 +263,10 @@ public void SaveTo(Stream stream) var endOfStream = stream.Position; - stream.Position = hash.Length + h.Length; // Position the stream in the first byte of the serialized data (i.e, skip *hash lenght* and *hash* + stream.Position = hash.Length + h.Length; // Position the stream in the first byte of the serialized data (i.e, skip *hash length* and *hash* hash = hasher.ComputeHash(stream); - stream.Position = h.Length; // position the stream past the *hash lenght* (i.e, *hash first byte*) + stream.Position = h.Length; // position the stream past the *hash length* (i.e, *hash first byte*) stream.Write(hash, 0, hash.Length); stream.Position = endOfStream; @@ -327,7 +345,8 @@ public override string ToString() } } - private List m_Graph; + List m_Graph; + HashSet m_Processed; // used to ignore cycles. } [Flags] diff --git a/Editor/Mono/Scripting/ScriptCompilation/DefineConstraintsHelper.cs b/Editor/Mono/Scripting/ScriptCompilation/DefineConstraintsHelper.cs index a9c5f1a00b..be94097fb0 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/DefineConstraintsHelper.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/DefineConstraintsHelper.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using UnityEditorInternal; using UnityEngine.Scripting; namespace UnityEditor.Scripting.ScriptCompilation @@ -18,6 +17,8 @@ static class DefineConstraintsHelper public static readonly char[] k_ValidWhitespaces = { ' ', '\t' }; + static Regex s_SplitAndKeep = new Regex("(\\|\\|)", RegexOptions.Compiled); + public enum DefineConstraintStatus { Compatible, @@ -52,7 +53,7 @@ static void GetDefineConstraintsCompatibility(string[] defines, string[] defineC internal static DefineConstraintStatus GetDefineConstraintCompatibility(string[] defines, string defineConstraints) { // Split by "||" (OR) and keep it in the resulting array - var splitDefines = Regex.Split(defineConstraints, "(\\|\\|)"); + var splitDefines = s_SplitAndKeep.Split(defineConstraints); // Trim what we consider valid space characters for (var i = 0; i < splitDefines.Length; ++i) @@ -69,8 +70,8 @@ internal static DefineConstraintStatus GetDefineConstraintCompatibility(string[] } } - var notExpectedDefines = new HashSet(splitDefines.Where(x => x.StartsWith(Not) && x != Or).Select(x => x.Substring(1))); - var expectedDefines = new HashSet(splitDefines.Where(x => !x.StartsWith(Not) && x != Or)); + var notExpectedDefines = new HashSet(splitDefines.Where(x => x.StartsWith(Not, StringComparison.Ordinal) && x != Or).Select(x => x.Substring(1))); + var expectedDefines = new HashSet(splitDefines.Where(x => !x.StartsWith(Not, StringComparison.Ordinal) && x != Or)); if (defines == null) { @@ -105,13 +106,31 @@ internal static DefineConstraintStatus GetDefineConstraintCompatibility(string[] notExpectedDefines.ExceptWith(complement); } - var expectedDefinesResult = expectedDefines.Any(defines.Contains) ? DefineConstraintStatus.Compatible : DefineConstraintStatus.Incompatible; + var expectedDefinesResult = DefineConstraintStatus.Incompatible; + foreach (var define in expectedDefines) + { + if (defines.Contains(define)) + { + expectedDefinesResult = DefineConstraintStatus.Compatible; + break; + } + } + if (expectedDefines.Count > 0 && notExpectedDefines.Count == 0) { return expectedDefinesResult; } - var notExpectedDefinesResult = notExpectedDefines.Any(defines.Contains) ? DefineConstraintStatus.Incompatible : DefineConstraintStatus.Compatible; + var notExpectedDefinesResult = DefineConstraintStatus.Compatible; + foreach (var define in notExpectedDefines) + { + if (defines.Contains(define)) + { + notExpectedDefinesResult = DefineConstraintStatus.Incompatible; + break; + } + } + if (notExpectedDefines.Count > 0 && expectedDefines.Count == 0) { return notExpectedDefinesResult; @@ -136,7 +155,7 @@ internal static bool IsDefineConstraintValid(string define) var splitDefines = define.Split(new[] { Or }, StringSplitOptions.RemoveEmptyEntries); foreach (var d in splitDefines) { - var finalDefine = (d.StartsWith(Not) ? d.Substring(1) : d).Trim(); + var finalDefine = (d.StartsWith(Not, StringComparison.Ordinal) ? d.Substring(1) : d).Trim(); if (!SymbolNameRestrictions.IsValid(finalDefine)) { return false; diff --git a/Editor/Mono/SerializedProperty.bindings.cs b/Editor/Mono/SerializedProperty.bindings.cs index 60024b275d..ba26759b26 100644 --- a/Editor/Mono/SerializedProperty.bindings.cs +++ b/Editor/Mono/SerializedProperty.bindings.cs @@ -921,10 +921,19 @@ public object managedReferenceValue var fieldInfo = UnityEditor.ScriptAttributeUtility.GetFieldInfoAndStaticTypeFromProperty(this, out type); var propertyBaseType = type; - if (value != null && !propertyBaseType.IsAssignableFrom(value.GetType())) + if (value != null) { - throw new System.InvalidOperationException( - $"Cannot assign an object of type '{value.GetType().Name}' to a managed reference with a base type of '{propertyBaseType.Name}': types are not compatible"); + var valueType = value.GetType(); + if (valueType == typeof(UnityObject) || valueType.IsSubclassOf(typeof(UnityObject))) + { + throw new System.InvalidOperationException( + $"Cannot assign an object deriving from UnityEngine.Object to a managed reference. This is not supported."); + } + else if (!propertyBaseType.IsAssignableFrom(valueType)) + { + throw new System.InvalidOperationException( + $"Cannot assign an object of type '{valueType.Name}' to a managed reference with a base type of '{propertyBaseType.Name}': types are not compatible"); + } } Verify(VerifyFlags.IteratorNotAtEnd); diff --git a/Editor/Mono/SettingsWindow/GraphicsSettingsEditors.cs b/Editor/Mono/SettingsWindow/GraphicsSettingsEditors.cs index 48a78b4b4a..51ed787bab 100644 --- a/Editor/Mono/SettingsWindow/GraphicsSettingsEditors.cs +++ b/Editor/Mono/SettingsWindow/GraphicsSettingsEditors.cs @@ -372,10 +372,11 @@ internal class TierSettingsEditor : Editor internal void OnFieldLabelsGUI(bool vertical) { + bool usingSRP = GraphicsSettings.currentRenderPipeline != null; + if (!vertical) EditorGUILayout.LabelField(Styles.standardShaderSettings, EditorStyles.boldLabel); - bool usingSRP = GraphicsSettings.currentRenderPipeline != null; if (!usingSRP) { EditorGUILayout.LabelField(Styles.standardShaderQuality); @@ -399,12 +400,7 @@ internal void OnFieldLabelsGUI(bool vertical) EditorGUILayout.LabelField(Styles.cascadedShadowMaps); EditorGUILayout.LabelField(Styles.prefer32BitShadowMaps); EditorGUILayout.LabelField(Styles.useHDR); - } - - EditorGUILayout.LabelField(Styles.hdrMode); - - if (!usingSRP) - { + EditorGUILayout.LabelField(Styles.hdrMode); EditorGUILayout.LabelField(Styles.renderingPath); } @@ -490,12 +486,9 @@ internal void OnTierGUI(BuildTargetGroup platform, GraphicsTier tier, bool verti ts.cascadedShadowMaps = EditorGUILayout.Toggle(ts.cascadedShadowMaps); ts.prefer32BitShadowMaps = EditorGUILayout.Toggle(ts.prefer32BitShadowMaps); ts.hdr = EditorGUILayout.Toggle(ts.hdr); - } - - ts.hdrMode = HDRModePopup(ts.hdrMode); - - if (!usingSRP) + ts.hdrMode = HDRModePopup(ts.hdrMode); ts.renderingPath = RenderingPathPopup(ts.renderingPath); + } if (SupportedRenderingFeatures.IsLightmapBakeTypeSupported(LightmapBakeType.Realtime)) ts.realtimeGICPUUsage = RealtimeGICPUUsagePopup(ts.realtimeGICPUUsage); @@ -562,6 +555,7 @@ internal void OnGuiVertical(BuildTargetGroup platform) autoSettings = EditorGUILayout.Toggle(Styles.autoSettings, autoSettings); GUILayout.EndHorizontal(); } + if (EditorGUI.EndChangeCheck()) { EditorGraphicsSettings.RegisterUndo(); diff --git a/Editor/Mono/TooltipView/TooltipView.cs b/Editor/Mono/TooltipView/TooltipView.cs index 43147cc5ec..30320c82f0 100644 --- a/Editor/Mono/TooltipView/TooltipView.cs +++ b/Editor/Mono/TooltipView/TooltipView.cs @@ -82,6 +82,15 @@ void Setup(string tooltip, Rect rect, GUIView hostView) popupPosition.y = Mathf.Max(popupPosition.y, Mathf.Floor(m_hoverRect.y + (m_hoverRect.height) + 10.0f)); } + // If when fitted to screen, the tooltip would overlap the hover area + // (and thus potentially mouse) -- for example when the control is near + // the bottom of screen, place it atop of the hover area instead. + var fittedToScreen = ContainerWindow.FitRectToScreen(popupPosition, true, true); + if (fittedToScreen.Overlaps(m_hoverRect)) + { + popupPosition.y = m_hoverRect.y - m_optimalSize.y - 10.0f; + } + window.position = popupPosition; position = new Rect(0, 0, m_optimalSize.x, m_optimalSize.y); diff --git a/Editor/Mono/UIElements/Controls/BindingExtensions.cs b/Editor/Mono/UIElements/Controls/BindingExtensions.cs index 5342f805d8..9b70cbb7f1 100644 --- a/Editor/Mono/UIElements/Controls/BindingExtensions.cs +++ b/Editor/Mono/UIElements/Controls/BindingExtensions.cs @@ -10,95 +10,14 @@ namespace UnityEditor.UIElements { - internal class SerializedObjectBindEvent : EventBase + internal class DefaultSerializedObjectBindingImplementation : ISerializedObjectBindingImplementation { - private SerializedObject m_BindObject; - public SerializedObject bindObject - { - get - { - return m_BindObject; - } - } - - protected override void Init() - { - base.Init(); - LocalInit(); - } - - void LocalInit() - { - this.propagation = EventPropagation.Cancellable; // Also makes it not propagatable. - m_BindObject = null; - } - - public static SerializedObjectBindEvent GetPooled(SerializedObject obj) - { - SerializedObjectBindEvent e = GetPooled(); - e.m_BindObject = obj; - return e; - } - - public SerializedObjectBindEvent() - { - LocalInit(); - } - } - - internal class SerializedPropertyBindEvent : EventBase - { - private SerializedProperty m_BindProperty; - public SerializedProperty bindProperty - { - get - { - return m_BindProperty; - } - } - - protected override void Init() - { - base.Init(); - LocalInit(); - } - - void LocalInit() - { - this.propagation = EventPropagation.Cancellable; // Also makes it not propagatable. - m_BindProperty = null; - } - - public static SerializedPropertyBindEvent GetPooled(SerializedProperty obj) - { - SerializedPropertyBindEvent e = GetPooled(); - e.m_BindProperty = obj; - return e; - } - - public SerializedPropertyBindEvent() - { - LocalInit(); - } - } - - public static class BindingExtensions - { - // visual element style changes wrt its property state - public static readonly string prefabOverrideUssClassName = "unity-binding--prefab-override"; - internal static readonly string prefabOverrideBarName = "unity-binding-prefab-override-bar"; - internal static readonly string prefabOverrideBarContainerName = "unity-prefab-override-bars-container"; - internal static readonly string prefabOverrideBarUssClassName = "unity-binding__prefab-override-bar"; - internal static readonly string animationAnimatedUssClassName = "unity-binding--animation-animated"; - internal static readonly string animationRecordedUssClassName = "unity-binding--animation-recorded"; - internal static readonly string animationCandidateUssClassName = "unity-binding--animation-candidate"; - - public static void Bind(this VisualElement element, SerializedObject obj) + public void Bind(VisualElement element, SerializedObject obj) { Bind(element, new SerializedObjectUpdateWrapper(obj), null); } - public static void Unbind(this VisualElement element) + public void Unbind(VisualElement element) { if (element == null) { @@ -114,7 +33,7 @@ public static void Unbind(this VisualElement element) } } - public static SerializedProperty BindProperty(this IBindable field, SerializedObject obj) + public SerializedProperty BindProperty(IBindable field, SerializedObject obj) { var property = obj?.FindProperty(field.bindingPath); @@ -126,7 +45,7 @@ public static SerializedProperty BindProperty(this IBindable field, SerializedOb return property; } - public static void BindProperty(this IBindable field, SerializedProperty property) + public void BindProperty(IBindable field, SerializedProperty property) { if (property == null) { @@ -137,6 +56,7 @@ public static void BindProperty(this IBindable field, SerializedProperty propert Bind(field as VisualElement, new SerializedObjectUpdateWrapper(property.serializedObject), null); } + // visual element style changes wrt its property state private static void DoBindProperty(IBindable field, SerializedObjectUpdateWrapper obj, SerializedProperty property) { var fieldElement = field as VisualElement; @@ -163,7 +83,12 @@ private static void DoBindProperty(IBindable field, SerializedObjectUpdateWrappe CreateBindingObjectForProperty(fieldElement, obj, property); } - internal static void Bind(VisualElement element, SerializedObjectUpdateWrapper objWrapper, SerializedProperty parentProperty) + void ISerializedObjectBindingImplementation.Bind(VisualElement element, object objWrapper, SerializedProperty parentProperty) + { + Bind(element, objWrapper as SerializedObjectUpdateWrapper, parentProperty); + } + + private void Bind(VisualElement element, SerializedObjectUpdateWrapper objWrapper, SerializedProperty parentProperty) { IBindable field = element as IBindable; @@ -194,7 +119,7 @@ internal static void Bind(VisualElement element, SerializedObjectUpdateWrapper o } } - private static SerializedProperty BindPropertyWithParent(IBindable field, SerializedObjectUpdateWrapper objWrapper, SerializedProperty parentProperty) + private SerializedProperty BindPropertyWithParent(IBindable field, SerializedObjectUpdateWrapper objWrapper, SerializedProperty parentProperty) { var property = parentProperty?.FindPropertyRelative(field.bindingPath); @@ -365,7 +290,7 @@ private static void DefaultBind(VisualElement element, SerializedObjectU } } - internal static void HandleStyleUpdate(VisualElement element) + public void HandleStyleUpdate(VisualElement element) { var bindable = element as IBindable; var binding = bindable?.binding as SerializedObjectBindingBase; @@ -688,7 +613,7 @@ private static void UpdatePrefabOverrideBarStyleEvent(GeometryChangedEvent evt) if (container == null) return; - var barContainer = container.Q(prefabOverrideBarContainerName); + var barContainer = container.Q(BindingExtensions.prefabOverrideBarContainerName); if (barContainer == null) return; @@ -762,12 +687,12 @@ internal static void UpdateElementStyle(VisualElement element, SerializedPropert // Handle prefab state. if (handlePrefabState) { - if (!element.ClassListContains(prefabOverrideUssClassName)) + if (!element.ClassListContains(BindingExtensions.prefabOverrideUssClassName)) { var container = FindPrefabOverrideBarCompatibleParent(element); var barContainer = container?.prefabOverrideBlueBarsContainer; - element.AddToClassList(prefabOverrideUssClassName); + element.AddToClassList(BindingExtensions.prefabOverrideUssClassName); if (container != null && barContainer != null) { @@ -779,12 +704,12 @@ internal static void UpdateElementStyle(VisualElement element, SerializedPropert // and move them down beside their respective field. var prefabOverrideBar = new VisualElement(); - prefabOverrideBar.name = prefabOverrideBarName; + prefabOverrideBar.name = BindingExtensions.prefabOverrideBarName; prefabOverrideBar.userData = element; - prefabOverrideBar.AddToClassList(prefabOverrideBarUssClassName); + prefabOverrideBar.AddToClassList(BindingExtensions.prefabOverrideBarUssClassName); barContainer.Add(prefabOverrideBar); - element.SetProperty(prefabOverrideBarName, prefabOverrideBar); + element.SetProperty(BindingExtensions.prefabOverrideBarName, prefabOverrideBar); // We need to try and set the bar style right away, even if the container // didn't compute its layout yet. This is for when the override is done after @@ -797,16 +722,16 @@ internal static void UpdateElementStyle(VisualElement element, SerializedPropert } } } - else if (element.ClassListContains(prefabOverrideUssClassName)) + else if (element.ClassListContains(BindingExtensions.prefabOverrideUssClassName)) { - element.RemoveFromClassList(prefabOverrideUssClassName); + element.RemoveFromClassList(BindingExtensions.prefabOverrideUssClassName); var container = FindPrefabOverrideBarCompatibleParent(element); var barContainer = container?.prefabOverrideBlueBarsContainer; if (container != null && barContainer != null) { - var prefabOverrideBar = element.GetProperty(prefabOverrideBarName) as VisualElement; + var prefabOverrideBar = element.GetProperty(BindingExtensions.prefabOverrideBarName) as VisualElement; if (prefabOverrideBar != null) prefabOverrideBar.RemoveFromHierarchy(); } @@ -827,9 +752,9 @@ internal static void UpdateElementStyle(VisualElement element, SerializedPropert bool candidate = AnimationMode.IsPropertyCandidate(prop.serializedObject.targetObject, prop.propertyPath); bool recording = AnimationMode.InAnimationRecording(); - inputElement.EnableInClassList(animationRecordedUssClassName, animated && recording); - inputElement.EnableInClassList(animationCandidateUssClassName, animated && !recording && candidate); - inputElement.EnableInClassList(animationAnimatedUssClassName, animated && !recording && !candidate); + inputElement.EnableInClassList(BindingExtensions.animationRecordedUssClassName, animated && recording); + inputElement.EnableInClassList(BindingExtensions.animationCandidateUssClassName, animated && !recording && candidate); + inputElement.EnableInClassList(BindingExtensions.animationAnimatedUssClassName, animated && !recording && !candidate); } protected bool IsPropertyValid() diff --git a/Editor/Mono/UIElements/Controls/BindingsInterface.cs b/Editor/Mono/UIElements/Controls/BindingsInterface.cs new file mode 100644 index 0000000000..4d4f087b8a --- /dev/null +++ b/Editor/Mono/UIElements/Controls/BindingsInterface.cs @@ -0,0 +1,135 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Linq; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; + +namespace UnityEditor.UIElements +{ + internal class SerializedObjectBindEvent : EventBase + { + private SerializedObject m_BindObject; + public SerializedObject bindObject + { + get + { + return m_BindObject; + } + } + + protected override void Init() + { + base.Init(); + LocalInit(); + } + + void LocalInit() + { + this.propagation = EventPropagation.Cancellable; // Also makes it not propagatable. + m_BindObject = null; + } + + public static SerializedObjectBindEvent GetPooled(SerializedObject obj) + { + SerializedObjectBindEvent e = GetPooled(); + e.m_BindObject = obj; + return e; + } + + public SerializedObjectBindEvent() + { + LocalInit(); + } + } + + internal class SerializedPropertyBindEvent : EventBase + { + private SerializedProperty m_BindProperty; + public SerializedProperty bindProperty + { + get + { + return m_BindProperty; + } + } + + protected override void Init() + { + base.Init(); + LocalInit(); + } + + void LocalInit() + { + this.propagation = EventPropagation.Cancellable; // Also makes it not propagatable. + m_BindProperty = null; + } + + public static SerializedPropertyBindEvent GetPooled(SerializedProperty obj) + { + SerializedPropertyBindEvent e = GetPooled(); + e.m_BindProperty = obj; + return e; + } + + public SerializedPropertyBindEvent() + { + LocalInit(); + } + } + + internal interface ISerializedObjectBindingImplementation + { + void Bind(VisualElement element, SerializedObject obj); + void Unbind(VisualElement element); + SerializedProperty BindProperty(IBindable field, SerializedObject obj); + void BindProperty(IBindable field, SerializedProperty property); + + void Bind(VisualElement element, object bindingContext, SerializedProperty parentProperty); + + void HandleStyleUpdate(VisualElement element); + } + + public static class BindingExtensions + { + public static readonly string prefabOverrideUssClassName = "unity-binding--prefab-override"; + internal static readonly string prefabOverrideBarName = "unity-binding-prefab-override-bar"; + internal static readonly string prefabOverrideBarContainerName = "unity-prefab-override-bars-container"; + internal static readonly string prefabOverrideBarUssClassName = "unity-binding__prefab-override-bar"; + internal static readonly string animationAnimatedUssClassName = "unity-binding--animation-animated"; + internal static readonly string animationRecordedUssClassName = "unity-binding--animation-recorded"; + internal static readonly string animationCandidateUssClassName = "unity-binding--animation-candidate"; + + internal static ISerializedObjectBindingImplementation bindingImpl = + new DefaultSerializedObjectBindingImplementation(); + + public static void Bind(this VisualElement element, SerializedObject obj) + { + bindingImpl.Bind(element, obj); + } + + public static void Unbind(this VisualElement element) + { + bindingImpl.Unbind(element); + } + + public static SerializedProperty BindProperty(this IBindable field, SerializedObject obj) + { + return bindingImpl.BindProperty(field, obj); + } + + public static void BindProperty(this IBindable field, SerializedProperty property) + { + bindingImpl.BindProperty(field, property); + } + + internal static void HandleStyleUpdate(VisualElement element) + { + bindingImpl.HandleStyleUpdate(element); + } + } +} diff --git a/Editor/Mono/UIElements/Controls/LayerMaskField.cs b/Editor/Mono/UIElements/Controls/LayerMaskField.cs index e4eb45dc7c..dc1afc9e81 100644 --- a/Editor/Mono/UIElements/Controls/LayerMaskField.cs +++ b/Editor/Mono/UIElements/Controls/LayerMaskField.cs @@ -14,7 +14,19 @@ public class LayerMaskField : MaskField { public new class UxmlFactory : UxmlFactory {} - public new class UxmlTraits : MaskField.UxmlTraits {} + public new class UxmlTraits : BasePopupField.UxmlTraits + { + readonly UxmlIntAttributeDescription m_MaskValue = new UxmlIntAttributeDescription { name = "value" }; + + public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) + { + var layerMaskField = (LayerMaskField)ve; + + // The mask is simply an int + layerMaskField.SetValueWithoutNotify(m_MaskValue.GetValueFromBag(bag, cc)); + base.Init(ve, bag, cc); + } + } public override Func formatSelectedValueCallback { diff --git a/Editor/Mono/UIElements/Controls/ListViewBindings.cs b/Editor/Mono/UIElements/Controls/ListViewBindings.cs index 005f432291..fa85870f87 100644 --- a/Editor/Mono/UIElements/Controls/ListViewBindings.cs +++ b/Editor/Mono/UIElements/Controls/ListViewBindings.cs @@ -11,7 +11,7 @@ namespace UnityEditor.UIElements { - class ListViewSerializedObjectBinding : BindingExtensions.SerializedObjectBindingBase + class ListViewSerializedObjectBinding : DefaultSerializedObjectBindingImplementation.SerializedObjectBindingBase { ListView listView { get { return boundElement as ListView; } set { boundElement = value; } } @@ -21,7 +21,7 @@ class ListViewSerializedObjectBinding : BindingExtensions.SerializedObjectBindin int m_ListViewArraySize; public static void CreateBind(ListView listView, - BindingExtensions.SerializedObjectUpdateWrapper objWrapper, + DefaultSerializedObjectBindingImplementation.SerializedObjectUpdateWrapper objWrapper, SerializedProperty prop) { var newBinding = new ListViewSerializedObjectBinding(); @@ -29,7 +29,7 @@ public static void CreateBind(ListView listView, } protected void SetBinding(ListView listView, - BindingExtensions.SerializedObjectUpdateWrapper objWrapper, + DefaultSerializedObjectBindingImplementation.SerializedObjectUpdateWrapper objWrapper, SerializedProperty prop) { boundObject = objWrapper; @@ -77,7 +77,7 @@ void BindListViewItem(VisualElement ve, int index) object item = listView.itemsSource[index]; var itemProp = item as SerializedProperty; field.bindingPath = itemProp.propertyPath; - BindingExtensions.Bind(ve, boundObject, itemProp); + BindingExtensions.bindingImpl.Bind(ve, boundObject, itemProp); } void UpdateArraySize() diff --git a/Editor/Mono/UIElements/UIElementsEditorWindowCreator/CSharpTemplateCreator.cs b/Editor/Mono/UIElements/UIElementsEditorWindowCreator/CSharpTemplateCreator.cs index 2eb4f3c775..679225aad0 100644 --- a/Editor/Mono/UIElements/UIElementsEditorWindowCreator/CSharpTemplateCreator.cs +++ b/Editor/Mono/UIElements/UIElementsEditorWindowCreator/CSharpTemplateCreator.cs @@ -2,14 +2,13 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License -using System; namespace UnityEditor.UIElements { static partial class UIElementsTemplate { public static string CreateCSharpTemplate(string cSharpName, string uxmlName, string ussName, string folder) { - string csTemplate = string.Format(@"using UnityEditor; + var csTemplate = string.Format(@"using UnityEditor; using UnityEngine; using UnityEngine.UIElements; using UnityEditor.UIElements; @@ -33,7 +32,7 @@ public void OnEnable() VisualElement label = new Label(""Hello World! From C#""); root.Add(label);", cSharpName); - if (uxmlName != String.Empty) + if (uxmlName != string.Empty) { csTemplate = csTemplate + string.Format(@" @@ -43,7 +42,7 @@ public void OnEnable() root.Add(labelFromUXML);", folder, uxmlName); } - if (ussName != String.Empty) + if (ussName != string.Empty) { csTemplate += string.Format(@" diff --git a/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UIElementsEditorWindowCreator.cs b/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UIElementsEditorWindowCreator.cs index cf9af0665d..972b82f1bf 100644 --- a/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UIElementsEditorWindowCreator.cs +++ b/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UIElementsEditorWindowCreator.cs @@ -19,18 +19,17 @@ class UIElementsEditorWindowCreator : EditorWindow VisualElement m_Root; VisualElement m_ErrorMessageBox; - string m_CSharpName = String.Empty; - string m_UxmlName = String.Empty; - string m_UssName = String.Empty; - - string m_Folder = String.Empty; - - string m_ErrorMessage = String.Empty; + string m_CSharpName = string.Empty; + string m_UxmlName = string.Empty; + string m_UssName = string.Empty; + string m_Folder = string.Empty; + string m_ErrorMessage = string.Empty; bool m_IsCSharpEnable = true; bool m_IsUssEnable = true; bool m_IsUxmlEnable = true; + [MenuItem("Assets/Create/UIElements/Editor Window", false, 701, false)] public static void CreateTemplateEditorWindow() { @@ -38,39 +37,34 @@ public static void CreateTemplateEditorWindow() CommandService.Execute(nameof(CreateTemplateEditorWindow), CommandHint.Menu); else { - UIElementsEditorWindowCreator editorWindow = GetWindow(true, "UIElements Editor Window Creator"); - editorWindow.maxSize = new Vector2(Styles.K_WindowWidth, Styles.K_WindowHeight); - editorWindow.minSize = new Vector2(Styles.K_WindowWidth, Styles.K_WindowHeight); - editorWindow.init(); + OpenCreateTemplateEditorWindow(); } } + + public static void OpenCreateTemplateEditorWindow() + { + var editorWindow = GetWindow(true, "UIElements Editor Window Creator"); + editorWindow.maxSize = new Vector2(Styles.K_WindowWidth, Styles.K_WindowHeight); + editorWindow.minSize = new Vector2(Styles.K_WindowWidth, Styles.K_WindowHeight); + editorWindow.init(); + } + public void init() { m_Folder = string.Empty; - - if (!ProjectWindowUtil.TryGetActiveFolderPath(out m_Folder)) + if (Selection.activeObject != null) { - if (Selection.activeObject != null) - { - m_Folder = AssetDatabase.GetAssetPath(Selection.activeObject); - - if (!AssetDatabase.IsValidFolder(m_Folder)) - { - m_Folder = Path.GetDirectoryName(m_Folder); - } - - if (!AssetDatabase.IsValidFolder(m_Folder)) - { - m_Folder = string.Empty; - } - } + m_Folder = AssetDatabase.GetAssetPath(Selection.activeObject); + if (!AssetDatabase.IsValidFolder(m_Folder)) + m_Folder = string.Empty; } if (string.IsNullOrEmpty(m_Folder)) - { + ProjectWindowUtil.TryGetActiveFolderPath(out m_Folder); + + if (string.IsNullOrEmpty(m_Folder) || m_Folder.Equals("Assets")) m_Folder = "Assets/Editor"; - } } public void OnEnable() @@ -79,7 +73,6 @@ public void OnEnable() if (m_CSharpName != "" && ClassExists()) { EditorApplication.ExecuteMenuItem("Window/UIElements/" + m_CSharpName); - EditorApplication.CallbackFunction handler = null; handler = () => { @@ -290,11 +283,6 @@ bool IsInputValid() return false; } - if (!IsValidPath()) - { - return false; - } - if (m_IsCSharpEnable && (!Validate(m_CSharpName, ".cs") || ClassExists())) { return false; @@ -313,17 +301,6 @@ bool IsInputValid() return true; } - bool IsValidPath() - { - if (m_Folder.Split('/').Contains("Editor") == false) - { - m_ErrorMessage = "The target path must be located inside an Editor folder"; - return false; - } - - return true; - } - bool IsAtLeastOneFileCreated() { bool isAtLeastOneFileCreated = m_IsCSharpEnable || m_IsUssEnable || m_IsUxmlEnable; diff --git a/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UssTemplateCreator.cs b/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UssTemplateCreator.cs index 2598e0232e..0da9ae5bd4 100644 --- a/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UssTemplateCreator.cs +++ b/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UssTemplateCreator.cs @@ -9,6 +9,7 @@ namespace UnityEditor.UIElements { static partial class UIElementsTemplate { + // Add submenu after GUI Skin [MenuItem("Assets/Create/UIElements/USS File", false, 603, false)] public static void CreateUSSFile() @@ -16,13 +17,17 @@ public static void CreateUSSFile() if (CommandService.Exists(nameof(CreateUSSFile))) CommandService.Execute(nameof(CreateUSSFile), CommandHint.Menu); else - { - var folder = GetCurrentFolder(); - var path = AssetDatabase.GenerateUniqueAssetPath(folder + "/NewUSSFile.uss"); - var contents = "VisualElement {}"; - var icon = EditorGUIUtility.IconContent().image as Texture2D; - ProjectWindowUtil.CreateAssetWithContent(path, contents, icon); - } + CreateUSSAsset(); + } + + + private static void CreateUSSAsset() + { + var folder = GetCurrentFolder(); + var path = AssetDatabase.GenerateUniqueAssetPath(folder + "/NewUSSFile.uss"); + var contents = "VisualElement {}"; + var icon = EditorGUIUtility.IconContent().image as Texture2D; + ProjectWindowUtil.CreateAssetWithContent(path, contents, icon); } } } diff --git a/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UxmlTemplateCreator.cs b/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UxmlTemplateCreator.cs index 4f2d7723ca..77e6a449d8 100644 --- a/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UxmlTemplateCreator.cs +++ b/Editor/Mono/UIElements/UIElementsEditorWindowCreator/UxmlTemplateCreator.cs @@ -36,19 +36,24 @@ private static string GetCurrentFolder() return filePath; } + [MenuItem("Assets/Create/UIElements/UXML Template", false, 610, false)] public static void CreateUXMLTemplate() { if (CommandService.Exists(nameof(CreateUXMLTemplate))) CommandService.Execute(nameof(CreateUXMLTemplate), CommandHint.Menu); else - { - var folder = GetCurrentFolder(); - var path = AssetDatabase.GenerateUniqueAssetPath(folder + "/NewUXMLTemplate.uxml"); - var contents = CreateUXMLTemplate(folder); - var icon = EditorGUIUtility.IconContent().image as Texture2D; - ProjectWindowUtil.CreateAssetWithContent(path, contents, icon); - } + CreateUXMAsset(); + } + + + private static void CreateUXMAsset() + { + var folder = GetCurrentFolder(); + var path = AssetDatabase.GenerateUniqueAssetPath(folder + "/NewUXMLTemplate.uxml"); + var contents = CreateUXMLTemplate(folder); + var icon = EditorGUIUtility.IconContent().image as Texture2D; + ProjectWindowUtil.CreateAssetWithContent(path, contents, icon); } public static string CreateUXMLTemplate(string folder, string uxmlContent = "") diff --git a/Editor/Mono/UIElements/UXMLEditorFactories.cs b/Editor/Mono/UIElements/UXMLEditorFactories.cs index 0be1e51467..0088842775 100644 --- a/Editor/Mono/UIElements/UXMLEditorFactories.cs +++ b/Editor/Mono/UIElements/UXMLEditorFactories.cs @@ -93,7 +93,12 @@ static UXMLEditorFactories() var types = TypeCache.GetTypesDerivedFrom(); foreach (var type in types) { - if (type.IsAbstract || !userAssemblies.Contains(type.Assembly.GetName().Name + ".dll")) + if (type.IsAbstract + || !userAssemblies.Contains(type.Assembly.GetName().Name + ".dll") + || !typeof(IUxmlFactory).IsAssignableFrom(type) + || type.IsInterface + || type.IsGenericType + || type.Assembly.GetName().Name == "UnityEngine.UIElementsModule") continue; var factory = (IUxmlFactory)Activator.CreateInstance(type); diff --git a/Editor/Mono/UIElements/WindowBackends/DefaultEditorWindowBackend.cs b/Editor/Mono/UIElements/WindowBackends/DefaultEditorWindowBackend.cs index 14af2ad468..a66b446932 100644 --- a/Editor/Mono/UIElements/WindowBackends/DefaultEditorWindowBackend.cs +++ b/Editor/Mono/UIElements/WindowBackends/DefaultEditorWindowBackend.cs @@ -53,7 +53,7 @@ public override void OnCreate(IWindowModel model) bool CurrentWindowHasCompatibleTree() { - return editorWindowModel.window.uiRootElement is VisualElement; + return editorWindowModel?.window.uiRootElement is VisualElement; } void RootVisualElementCreated() @@ -100,23 +100,37 @@ private void OnRegisterWindow() const TrickleDown k_TricklePhase = TrickleDown.TrickleDown; + private VisualElement m_RegisteredRoot; + private void AddRootElement(VisualElement root) { if (CurrentWindowHasCompatibleTree()) { + RemoveRootElement(m_RegisteredRoot); + + m_RegisteredRoot = root; root.RegisterCallback(SendEventToSplitterGUI, k_TricklePhase); root.RegisterCallback(SendEventToSplitterGUI, k_TricklePhase); root.RegisterCallback(SendEventToSplitterGUI, k_TricklePhase); + m_Panel.visualTree.Add(root); } } private void RemoveRootElement(VisualElement root) { + if (root == null) + return; + root.RemoveFromHierarchy(); - root.UnregisterCallback(SendEventToSplitterGUI, k_TricklePhase); - root.UnregisterCallback(SendEventToSplitterGUI, k_TricklePhase); - root.UnregisterCallback(SendEventToSplitterGUI, k_TricklePhase); + + if (root == m_RegisteredRoot) + { + m_RegisteredRoot = null; + root.UnregisterCallback(SendEventToSplitterGUI, k_TricklePhase); + root.UnregisterCallback(SendEventToSplitterGUI, k_TricklePhase); + root.UnregisterCallback(SendEventToSplitterGUI, k_TricklePhase); + } } private void SendEventToSplitterGUI(EventBase ev) @@ -124,6 +138,12 @@ private void SendEventToSplitterGUI(EventBase ev) if (ev.imguiEvent == null || ev.imguiEvent.rawType == EventType.Used) return; + if (imguiContainer == null || editorWindowModel == null) + { + RemoveRootElement(m_RegisteredRoot); + return; + } + imguiContainer.HandleIMGUIEvent(ev.imguiEvent, editorWindowModel.onSplitterGUIHandler, false); if (ev.imguiEvent.rawType == EventType.Used) @@ -172,6 +192,8 @@ public override void OnDestroy(IWindowModel model) m_NotificationContainer.onGUIHandler = null; m_NotificationContainer.RemoveFromHierarchy(); + RemoveRootElement(m_RegisteredRoot); + base.OnDestroy(model); } diff --git a/Editor/Mono/UIElements/WindowBackends/DefaultWindowBackend.cs b/Editor/Mono/UIElements/WindowBackends/DefaultWindowBackend.cs index 33e6ded07c..58cd08eb92 100644 --- a/Editor/Mono/UIElements/WindowBackends/DefaultWindowBackend.cs +++ b/Editor/Mono/UIElements/WindowBackends/DefaultWindowBackend.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; +using UnityEngine.Assertions; using UnityEngine.UIElements; namespace UnityEditor.UIElements @@ -32,6 +33,8 @@ public virtual void OnCreate(IWindowModel model) imguiContainer.focusOnlyIfHasFocusableControls = false; m_Panel.visualTree.Insert(0, imguiContainer); + Assert.IsNull(m_Panel.rootIMGUIContainer); + m_Panel.rootIMGUIContainer = imguiContainer; m_Model.sizeChanged = OnSizeChanged; m_Model.eventInterestsChanged = OnEventsInterestsChanged; @@ -54,13 +57,15 @@ public virtual void OnDestroy(IWindowModel model) if (imguiContainer.HasMouseCapture()) imguiContainer.ReleaseMouse(); imguiContainer.RemoveFromHierarchy(); + Assert.AreEqual(imguiContainer, m_Panel.rootIMGUIContainer); + m_Panel.rootIMGUIContainer = null; imguiContainer = null; } if (m_Model != null) { - m_Model.sizeChanged = OnSizeChanged; - m_Model.eventInterestsChanged = OnEventsInterestsChanged; + m_Model.sizeChanged = null; + m_Model.eventInterestsChanged = null; m_Model = null; } m_Panel.Dispose(); diff --git a/Modules/AR/Tango/ScriptBindings/Tango.bindings.cs b/Modules/AR/ARCore/ScriptBindings/ARCore.bindings.cs similarity index 96% rename from Modules/AR/Tango/ScriptBindings/Tango.bindings.cs rename to Modules/AR/ARCore/ScriptBindings/ARCore.bindings.cs index 6480aae5d6..f6503a298b 100644 --- a/Modules/AR/Tango/ScriptBindings/Tango.bindings.cs +++ b/Modules/AR/ARCore/ScriptBindings/ARCore.bindings.cs @@ -45,7 +45,7 @@ public Vector3 position } } - [NativeHeader("Modules/AR/Tango/TangoScriptApi.h")] + [NativeHeader("Modules/AR/ARCore/ARCoreScriptApi.h")] [NativeConditional("PLATFORM_ANDROID")] internal static partial class TangoInputTracking { diff --git a/Modules/AR/ScriptBindings/ARBackgroundRenderer.cs b/Modules/AR/ScriptBindings/ARBackgroundRenderer.cs deleted file mode 100644 index 54c32681ef..0000000000 --- a/Modules/AR/ScriptBindings/ARBackgroundRenderer.cs +++ /dev/null @@ -1,204 +0,0 @@ -// Unity C# reference source -// Copyright (c) Unity Technologies. For terms of use, see -// https://unity3d.com/legal/licenses/Unity_Reference_Only_License - -using System; -using UnityEngine.Rendering; - -namespace UnityEngine.XR -{ - // Must match ARRenderMode in Modules/AR/ARTypes.h - public enum ARRenderMode - { - StandardBackground, - MaterialAsBackground - } - - public partial class ARBackgroundRenderer - { - protected Camera m_Camera = null; - protected Material m_BackgroundMaterial = null; - protected Texture m_BackgroundTexture = null; - private ARRenderMode m_RenderMode = ARRenderMode.StandardBackground; - private CommandBuffer m_CommandBuffer = null; - private CameraClearFlags m_CameraClearFlags = CameraClearFlags.Skybox; - - // Actions to be subscribed to by AR platform code - public event Action backgroundRendererChanged = null; - - // Apply a new Material and reset the command buffers if needed - public Material backgroundMaterial - { - get - { - return m_BackgroundMaterial; - } - set - { - if (m_BackgroundMaterial == value) - return; - - RemoveCommandBuffersIfNeeded(); - m_BackgroundMaterial = value; - - if (backgroundRendererChanged != null) - backgroundRendererChanged(); - - ReapplyCommandBuffersIfNeeded(); - } - } - - // Apply a new Texture and reset the command buffers if needed - public Texture backgroundTexture - { - get - { - return m_BackgroundTexture; - } - set - { - if (m_BackgroundTexture == value) - return; - - RemoveCommandBuffersIfNeeded(); - m_BackgroundTexture = value; - - if (backgroundRendererChanged != null) - backgroundRendererChanged(); - - ReapplyCommandBuffersIfNeeded(); - } - } - - // Apply a new Camera and reset the command buffers if needed - public Camera camera - { - get - { - // Return main camera when no Camera has been set - return (m_Camera != null) ? m_Camera : Camera.main; - } - set - { - if (m_Camera == value) - return; - - RemoveCommandBuffersIfNeeded(); - m_Camera = value; - - if (backgroundRendererChanged != null) - backgroundRendererChanged(); - - ReapplyCommandBuffersIfNeeded(); - } - } - - // Apply a new render mode and reset the command buffers if needed - public ARRenderMode mode - { - get - { - return m_RenderMode; - } - set - { - if (value == m_RenderMode) - return; - - m_RenderMode = value; - - switch (m_RenderMode) - { - case ARRenderMode.StandardBackground: - DisableARBackgroundRendering(); - break; - case ARRenderMode.MaterialAsBackground: - EnableARBackgroundRendering(); - break; - default: - throw new Exception("Unhandled render mode."); - } - - if (backgroundRendererChanged != null) - backgroundRendererChanged(); - } - } - - protected bool EnableARBackgroundRendering() - { - if (m_BackgroundMaterial == null) - return false; - - Camera camera; - - if (m_Camera != null) - camera = m_Camera; - else - camera = Camera.main; - - if (camera == null) - return false; - - // Clear flags - m_CameraClearFlags = camera.clearFlags; - camera.clearFlags = CameraClearFlags.Depth; - - // Command buffer setup - m_CommandBuffer = new CommandBuffer(); - - var backgroundTexture = m_BackgroundTexture; - if (backgroundTexture == null) - { - const string kMainTexName = "_MainTex"; - - // GetTexture will return null if the texture isn't found, but it also - // writes an error to the console. We check for existence to silence - // this error. - if (m_BackgroundMaterial.HasProperty(kMainTexName)) - backgroundTexture = m_BackgroundMaterial.GetTexture(kMainTexName); - } - - m_CommandBuffer.Blit(backgroundTexture, BuiltinRenderTextureType.CameraTarget, m_BackgroundMaterial); - camera.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, m_CommandBuffer); - camera.AddCommandBuffer(CameraEvent.BeforeGBuffer, m_CommandBuffer); - - return true; - } - - protected void DisableARBackgroundRendering() - { - if (null == m_CommandBuffer) - return; - - var cam = m_Camera ?? Camera.main; - if (cam == null) - return; - - cam.clearFlags = m_CameraClearFlags; - - // Command buffer - cam.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, m_CommandBuffer); - cam.RemoveCommandBuffer(CameraEvent.BeforeGBuffer, m_CommandBuffer); - } - - private bool ReapplyCommandBuffersIfNeeded() - { - if (m_RenderMode != ARRenderMode.MaterialAsBackground) - return false; - - EnableARBackgroundRendering(); - - return true; - } - - private bool RemoveCommandBuffersIfNeeded() - { - if (m_RenderMode != ARRenderMode.MaterialAsBackground) - return false; - - DisableARBackgroundRendering(); - - return true; - } - } -} diff --git a/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs b/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs index f20144a368..768737a28d 100644 --- a/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs +++ b/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs @@ -255,6 +255,9 @@ static public bool OpenAsset(Object[] objects) extern public static string GUIDToAssetPath(string guid); extern public static Hash128 GetAssetDependencyHash(string path); + extern internal static Hash128 GetSourceAssetFileHash(string guid); + extern internal static Hash128 GetSourceAssetMetaFileHash(string guid); + [FreeFunction("AssetDatabase::SaveAssets")] extern public static void SaveAssets(); diff --git a/Modules/AssetPipelineEditor/ImportSettings/ModelImporterClipEditor.cs b/Modules/AssetPipelineEditor/ImportSettings/ModelImporterClipEditor.cs index 294583e79b..0669049776 100644 --- a/Modules/AssetPipelineEditor/ImportSettings/ModelImporterClipEditor.cs +++ b/Modules/AssetPipelineEditor/ImportSettings/ModelImporterClipEditor.cs @@ -18,17 +18,7 @@ internal class ModelImporterClipEditor : BaseAssetImporterTabUI AnimationClipEditor m_AnimationClipEditor; ModelImporter singleImporter { get { return targets[0] as ModelImporter; } } - private int m_SelectedClipIndexDoNotUseDirectly = -1; - public int selectedClipIndex - { - get { return m_SelectedClipIndexDoNotUseDirectly; } - set - { - m_SelectedClipIndexDoNotUseDirectly = Mathf.Clamp(value, 0, m_ClipAnimations.arraySize - 1); - if (m_ClipList != null) - m_ClipList.index = value; - } - } + internal const string ActiveClipIndex = "ModelImporterClipEditor.ActiveClipIndex"; public string selectedClipName { @@ -229,7 +219,17 @@ public Styles() public ModelImporterClipEditor(AssetImporterEditor panelContainer) : base(panelContainer) - {} + { + //Generate new Clip List + m_ClipList = new ReorderableList(new List(), typeof(string), false, true, true, true); + m_ClipList.onAddCallback = AddClipInList; + m_ClipList.onSelectCallback = SelectClipInList; + m_ClipList.onRemoveCallback = RemoveClipInList; + m_ClipList.drawElementCallback = DrawClipElement; + m_ClipList.drawHeaderCallback = DrawClipHeader; + m_ClipList.elementHeight = EditorGUI.kSingleLineHeight; + } + internal override void OnEnable() { Editor.AssignCachedProperties(this, serializedObject.GetIterator()); @@ -237,34 +237,21 @@ internal override void OnEnable() if (serializedObject.isEditingMultipleObjects) return; - // Find all serialized property before calling SetupDefaultClips - if (m_ClipAnimations.arraySize == 0) - SetupDefaultClips(); - UpdateList(); - - selectedClipIndex = EditorPrefs.GetInt("ModelImporterClipEditor.ActiveClipIndex", 0); - EditorPrefs.SetInt("ModelImporterClipEditor.ActiveClipIndex", selectedClipIndex); - - // Automatically select the first clip - if (m_ClipAnimations.arraySize != 0) - SelectClip(selectedClipIndex); + //Sometimes we dont want to start at the 0th index, this is where we're editing a clip - see + m_ClipList.index = EditorPrefs.GetInt(ActiveClipIndex, 0); + EditorPrefs.SetInt(ActiveClipIndex, m_ClipList.index); + //Reset the Model Importer to its serialized copy + DeserializeClips(); string[] transformPaths = singleImporter.transformPaths; m_MotionNodeList = new GUIContent[transformPaths.Length + 1]; m_MotionNodeList[0] = EditorGUIUtility.TrTextContent(""); + if (m_MotionNodeList.Length > 1) + m_MotionNodeList[1] = EditorGUIUtility.TrTextContent(""); - for (int i = 0; i < transformPaths.Length; i++) - { - if (i == 0) - { - m_MotionNodeList[1] = EditorGUIUtility.TrTextContent(""); - } - else - { - m_MotionNodeList[i + 1] = new GUIContent(transformPaths[i]); - } - } + for (int i = 1; i < transformPaths.Length; i++) + m_MotionNodeList[i + 1] = new GUIContent(transformPaths[i]); motionNodeIndex = ArrayUtility.FindIndex(m_MotionNodeList, delegate(GUIContent content) { return content.text == m_MotionNodeName.stringValue; }); motionNodeIndex = motionNodeIndex < 1 ? 0 : motionNodeIndex; @@ -274,6 +261,7 @@ internal override void OnEnable() m_Warnings = m_AnimationImportWarnings.stringValue; m_RigWarnings = m_RigImportWarnings.stringValue; m_RetargetWarnings = m_AnimationRetargetingWarnings.stringValue; + RegisterListeners(); } void SyncClipEditor(AnimationClipInfoProperties info) @@ -334,8 +322,10 @@ private void TransferDefaultClipsToCustomClips() m_DefaultClipsSerializedObject = null; PatchDefaultClipTakeNamesToSplitClipNames(); + UpdateList(); - SyncClipEditor(((ClipInformation)m_ClipList.list[selectedClipIndex]).property); + if (m_ClipList.index >= 0) + SyncClipEditor(((ClipInformation)m_ClipList.list[m_ClipList.index]).property); } internal override void OnDestroy() @@ -345,6 +335,7 @@ internal override void OnDestroy() internal override void OnDisable() { + UnregisterListeners(); DestroyEditorsAndData(); base.OnDisable(); @@ -353,15 +344,7 @@ internal override void OnDisable() internal override void ResetValues() { base.ResetValues(); - m_ClipAnimations = serializedObject.FindProperty("m_ClipAnimations"); - m_AnimationType = serializedObject.FindProperty("m_AnimationType"); - m_DefaultClipsSerializedObject = null; - if (m_ClipAnimations.arraySize == 0) - SetupDefaultClips(); - - selectedClipIndex = selectedClipIndex; - UpdateList(); - SelectClip(selectedClipIndex); + DeserializeClips(); } void AnimationClipGUI() @@ -436,12 +419,17 @@ public override void OnInspectorGUI() styles = new Styles(); EditorGUILayout.PropertyField(m_ImportConstraints, styles.ImportConstraints); - EditorGUILayout.PropertyField(m_ImportAnimation, styles.ImportAnimations); + + using (var check = new EditorGUI.ChangeCheckScope()) + { + EditorGUILayout.PropertyField(m_ImportAnimation, styles.ImportAnimations); + if (check.changed) + DeserializeClips(); + } if (m_ImportAnimation.boolValue && !m_ImportAnimation.hasMultipleDifferentValues) { bool hasNoValidAnimationData = targets.Length == 1 && singleImporter.importedTakeInfos.Length == 0 && singleImporter.animationType != ModelImporterAnimationType.None; - if (IsDeprecatedMultiAnimationRootImport()) EditorGUILayout.HelpBox(styles.AnimationDataWas); else if (hasNoValidAnimationData) @@ -475,6 +463,12 @@ public override void OnInspectorGUI() } } + internal override void PostApply() + { + base.PostApply(); + DeserializeClips(); + } + void AnimationSettings() { EditorGUILayout.Space(); @@ -583,40 +577,24 @@ void SelectClip(int selected) if (EditorGUI.s_DelayedTextEditor != null && Event.current != null) EditorGUI.s_DelayedTextEditor.EndGUI(Event.current.type); - if (selected != selectedClipIndex || m_AnimationClipEditor == null) - { - DestroyEditorsAndData(); + DestroyEditorsAndData(); - selectedClipIndex = selected; - if (selectedClipIndex < 0 || selectedClipIndex >= m_ClipAnimations.arraySize) - { - selectedClipIndex = -1; - return; - } + m_ClipList.index = selected; + if (m_ClipList.index < 0) + return; - AnimationClipInfoProperties info = ((ClipInformation)m_ClipList.list[selectedClipIndex]).property; - AnimationClip clip = singleImporter.GetPreviewAnimationClipForTake(info.takeName); - if (clip != null) - { - m_AnimationClipEditor = (AnimationClipEditor)Editor.CreateEditor(clip, typeof(AnimationClipEditor)); - InitMask(info); - SyncClipEditor(info); - } + AnimationClipInfoProperties info = ((ClipInformation)m_ClipList.list[m_ClipList.index]).property; + AnimationClip clip = singleImporter.GetPreviewAnimationClipForTake(info.takeName); + if (clip != null) + { + m_AnimationClipEditor = (AnimationClipEditor)Editor.CreateEditor(clip, typeof(AnimationClipEditor)); + InitMask(info); + SyncClipEditor(info); } } void UpdateList() { - if (m_ClipList == null) - { - m_ClipList = new ReorderableList(new List(), typeof(string), false, true, true, true); - m_ClipList.onAddCallback = AddClipInList; - m_ClipList.onSelectCallback = SelectClipInList; - m_ClipList.onRemoveCallback = RemoveClipInList; - m_ClipList.drawElementCallback = DrawClipElement; - m_ClipList.drawHeaderCallback = DrawClipHeader; - m_ClipList.elementHeight = 16; - } List clipInfos = new List(); var prop = m_ClipAnimations.FindPropertyRelative("Array.size"); for (int i = 0; i < m_ClipAnimations.arraySize; i++) @@ -625,8 +603,7 @@ void UpdateList() clipInfos.Add(new ClipInformation(prop.Copy())); } m_ClipList.list = clipInfos; - selectedClipIndex = selectedClipIndex; - m_ClipList.index = selectedClipIndex; + m_ClipList.index = Mathf.Clamp(m_ClipList.index, -1, m_ClipAnimations.arraySize - 1); } void AddClipInList(ReorderableList list) @@ -636,9 +613,9 @@ void AddClipInList(ReorderableList list) int takeIndex = 0; - if (0 < selectedClipIndex && selectedClipIndex < m_ClipAnimations.arraySize) + if (0 < m_ClipList.index && m_ClipList.index < m_ClipAnimations.arraySize) { - AnimationClipInfoProperties info = ((ClipInformation)m_ClipList.list[selectedClipIndex]).property; + AnimationClipInfoProperties info = ((ClipInformation)m_ClipList.list[m_ClipList.index]).property; for (int i = 0; i < singleImporter.importedTakeInfos.Length; i++) { if (singleImporter.importedTakeInfos[i].name == info.takeName) @@ -650,7 +627,6 @@ void AddClipInList(ReorderableList list) } AddClip(singleImporter.importedTakeInfos[takeIndex]); - UpdateList(); SelectClip(list.list.Count - 1); } @@ -659,7 +635,6 @@ void RemoveClipInList(ReorderableList list) TransferDefaultClipsToCustomClips(); RemoveClip(list.index); - UpdateList(); SelectClip(Mathf.Min(list.index, list.count - 1)); } @@ -705,7 +680,7 @@ void AnimationSplitTable() if (clip == null) return; - if (m_AnimationClipEditor != null && selectedClipIndex != -1) + if (m_AnimationClipEditor != null) { GUILayout.Space(5); @@ -751,7 +726,7 @@ void AnimationSplitTable() clip.name = MakeUniqueClipName(takeNames[newTakeIndex]); SetupTakeNameAndFrames(clip, importedTakeInfos[newTakeIndex]); GUIUtility.keyboardControl = 0; - SelectClip(selectedClipIndex); + SelectClip(m_ClipList.index); // actualClip has been changed by SelectClip actualClip = m_AnimationClipEditor.target as AnimationClip; @@ -764,13 +739,13 @@ void AnimationSplitTable() if (!actualClip.legacy) clip.ExtractFromPreviewClip(actualClip); - } - } - if (EditorGUI.EndChangeCheck() || m_AnimationClipEditor.needsToGenerateClipInfo) - { - TransferDefaultClipsToCustomClips(); - m_AnimationClipEditor.needsToGenerateClipInfo = false; + if (EditorGUI.EndChangeCheck() || m_AnimationClipEditor.needsToGenerateClipInfo) + { + TransferDefaultClipsToCustomClips(); + m_AnimationClipEditor.needsToGenerateClipInfo = false; + } + } } } @@ -795,13 +770,13 @@ bool IsDeprecatedMultiAnimationRootImport() public override void OnInteractivePreviewGUI(Rect r, GUIStyle background) { - if (m_AnimationClipEditor) - m_AnimationClipEditor.OnInteractivePreviewGUI(r, background); + m_AnimationClipEditor.OnInteractivePreviewGUI(r, background); } AnimationClipInfoProperties GetSelectedClipInfo() { - return ((ClipInformation)m_ClipList?.list[m_ClipList.index])?.property; + //If it doesn't have a selected clip. return null - there is nothing to select! + return m_ClipList.index >= 0 && m_ClipList.index < m_ClipList.count ? ((ClipInformation)m_ClipList.list[m_ClipList.index]).property : null; } /// @@ -849,7 +824,7 @@ string FindNextAvailableName(string baseName) string[] allClipNames = new string[m_ClipAnimations.arraySize]; for (int i = 0; i < m_ClipAnimations.arraySize; ++i) { - AnimationClipInfoProperties clip = ((ClipInformation)m_ClipList.list[selectedClipIndex]).property; + AnimationClipInfoProperties clip = ((ClipInformation)m_ClipList.list[i]).property; allClipNames[i] = clip.name; } Array.Sort(allClipNames, StringComparer.InvariantCulture); @@ -884,6 +859,7 @@ void RemoveClip(int index) SetupDefaultClips(); m_ImportAnimation.boolValue = false; } + UpdateList(); } void SetupTakeNameAndFrames(AnimationClipInfoProperties info, TakeInfo takeInfo) @@ -923,9 +899,9 @@ void AddClip(TakeInfo takeInfo) SetBodyMaskDefaultValues(info); - info.ClearEvents(); info.ClearCurves(); + UpdateList(); } private AvatarMask m_Mask = null; @@ -1041,5 +1017,43 @@ private void SetBodyMaskDefaultValues(AnimationClipInfoProperties clipInfo) bodyMask.GetArrayElementAtIndex(i).intValue = 1; } } + + void RegisterListeners() + { + //Ensures that the ClipList and the Serialized copy of the clip remain in sync when an Undo/Redo is performed. + Undo.undoRedoPerformed += HandleUndo; + } + + void UnregisterListeners() + { + Undo.undoRedoPerformed -= HandleUndo; + } + + void HandleUndo() + { + //Update animations serialization in-case something has changed + m_ClipAnimations.serializedObject.UpdateIfRequiredOrScript(); + + //Reset the cache to the serialized values + DeserializeClips(); + } + + void DeserializeClips() + { + //Clear the clip editors + DestroyEditorsAndData(); + + //Reload the clips + m_ClipAnimations = serializedObject.FindProperty("m_ClipAnimations"); + m_AnimationType = serializedObject.FindProperty("m_AnimationType"); + m_DefaultClipsSerializedObject = null; + if (m_ClipAnimations.arraySize == 0) + SetupDefaultClips(); + UpdateList(); + + //Set the active clip within a valid range, -1 ONLY if there are no possible clips to select. + int selectedClip = m_ClipList.count > 0 ? Mathf.Clamp(m_ClipList.index, 0, m_ClipList.count) : -1; + SelectClip(selectedClip); + } } } diff --git a/Modules/IMGUI/EventInterests.cs b/Modules/IMGUI/EventInterests.cs index 14fa1dd486..a20c6b2ceb 100644 --- a/Modules/IMGUI/EventInterests.cs +++ b/Modules/IMGUI/EventInterests.cs @@ -2,14 +2,13 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License -using UnityEngine.Bindings; - namespace UnityEngine { internal struct EventInterests { public bool wantsMouseMove { get; set; } public bool wantsMouseEnterLeaveWindow { get; set; } + public bool wantsLessLayoutEvents { get; set; } public bool WantsEvent(EventType type) { @@ -24,5 +23,31 @@ public bool WantsEvent(EventType type) return true; } } + + public bool WantsLayoutPass(EventType type) + { + if (!wantsLessLayoutEvents) + return true; + + switch (type) + { + case EventType.Repaint: + return true; + + case EventType.KeyDown: + case EventType.KeyUp: + return GUIUtility.textFieldInput; + + case EventType.MouseDown: + case EventType.MouseUp: + return wantsMouseMove; + + case EventType.MouseEnterWindow: + case EventType.MouseLeaveWindow: + return wantsMouseEnterLeaveWindow; + } + + return false; + } } } diff --git a/Modules/ParticleSystem/ScriptBindings/ParticleSystem.bindings.cs b/Modules/ParticleSystem/ScriptBindings/ParticleSystem.bindings.cs index 3a7f2c2104..9e9d1c60b5 100644 --- a/Modules/ParticleSystem/ScriptBindings/ParticleSystem.bindings.cs +++ b/Modules/ParticleSystem/ScriptBindings/ParticleSystem.bindings.cs @@ -8,6 +8,8 @@ using UnityEngine.Bindings; using UnityEngine.Scripting; using RequiredByNativeCodeAttribute = UnityEngine.Scripting.RequiredByNativeCodeAttribute; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; using Unity.Jobs.LowLevel.Unsafe; @@ -86,7 +88,7 @@ extern public bool proceduralSimulationSupported // Mesh index helper [FreeFunction(Name = "ParticleSystemScriptBindings::GetParticleMeshIndex", HasExplicitThis = true)] - extern internal int GetParticleMeshIndex(ref ParticleSystem.Particle particle); + extern internal int GetParticleMeshIndex(ref Particle particle); // Set/get particles [FreeFunction(Name = "ParticleSystemScriptBindings::SetParticles", HasExplicitThis = true, ThrowsException = true)] @@ -94,11 +96,23 @@ extern public bool proceduralSimulationSupported public void SetParticles([Out] Particle[] particles, int size) { SetParticles(particles, size, 0); } public void SetParticles([Out] Particle[] particles) { SetParticles(particles, -1); } + [FreeFunction(Name = "ParticleSystemScriptBindings::SetParticlesWithNativeArray", HasExplicitThis = true, ThrowsException = true)] + extern private void SetParticlesWithNativeArray(IntPtr particles, int particlesLength, int size, int offset); + unsafe public void SetParticles([Out] NativeArray particles, int size, int offset) { SetParticlesWithNativeArray((IntPtr)particles.GetUnsafeReadOnlyPtr(), particles.Length, size, 0); } + public void SetParticles([Out] NativeArray particles, int size) { SetParticles(particles, size, 0); } + public void SetParticles([Out] NativeArray particles) { SetParticles(particles, -1); } + [FreeFunction(Name = "ParticleSystemScriptBindings::GetParticles", HasExplicitThis = true, ThrowsException = true)] extern public int GetParticles([NotNull][Out] Particle[] particles, int size, int offset); public int GetParticles([Out] Particle[] particles, int size) { return GetParticles(particles, size, 0); } public int GetParticles([Out] Particle[] particles) { return GetParticles(particles, -1); } + [FreeFunction(Name = "ParticleSystemScriptBindings::GetParticlesWithNativeArray", HasExplicitThis = true, ThrowsException = true)] + extern private int GetParticlesWithNativeArray(IntPtr particles, int particlesLength, int size, int offset); + unsafe public int GetParticles([Out] NativeArray particles, int size, int offset) { return GetParticlesWithNativeArray((IntPtr)particles.GetUnsafeReadOnlyPtr(), particles.Length, size, 0); } + public int GetParticles([Out] NativeArray particles, int size) { return GetParticles(particles, size, 0); } + public int GetParticles([Out] NativeArray particles) { return GetParticles(particles, -1); } + // Set/get custom particle data [FreeFunction(Name = "ParticleSystemScriptBindings::SetCustomParticleData", HasExplicitThis = true, ThrowsException = true)] extern public void SetCustomParticleData([NotNull] List customData, ParticleSystemCustomData streamIndex); diff --git a/Modules/ParticleSystemEditor/ParticleSystemModules/RendererModuleUI.cs b/Modules/ParticleSystemEditor/ParticleSystemModules/RendererModuleUI.cs index f0b1183b07..993be7699b 100644 --- a/Modules/ParticleSystemEditor/ParticleSystemModules/RendererModuleUI.cs +++ b/Modules/ParticleSystemEditor/ParticleSystemModules/RendererModuleUI.cs @@ -353,7 +353,7 @@ override public void OnInspectorGUI(InitialModuleUI initial) if (renderMode == RenderMode.Billboard) GUIToggle(s_Texts.allowRoll, m_AllowRoll); - if (renderMode == RenderMode.Mesh) + if (renderMode == RenderMode.Mesh && SupportedRenderingFeatures.active.particleSystemInstancing) GUIToggle(s_Texts.enableGPUInstancing, m_EnableGPUInstancing); } diff --git a/Modules/ParticleSystemEditor/ParticleSystemModules/UVModuleUI.cs b/Modules/ParticleSystemEditor/ParticleSystemModules/UVModuleUI.cs index 87f44b0f67..18e3cd8cfd 100644 --- a/Modules/ParticleSystemEditor/ParticleSystemModules/UVModuleUI.cs +++ b/Modules/ParticleSystemEditor/ParticleSystemModules/UVModuleUI.cs @@ -171,6 +171,12 @@ override public void OnInspectorGUI(InitialModuleUI initial) private void DoListOfSpritesGUI() { + if (m_Sprites.hasMultipleDifferentValues) + { + EditorGUILayout.HelpBox("Sprite editing is only available when all selected Particle Systems contain the same number of Sprites.", MessageType.Info, true); + return; + } + for (int i = 0; i < m_Sprites.arraySize; i++) { GUILayout.BeginHorizontal(); @@ -203,6 +209,8 @@ private void DoListOfSpritesGUI() private void ValidateSpriteList() { + if (m_Sprites.hasMultipleDifferentValues) + return; if (m_Sprites.arraySize <= 1) return; diff --git a/Modules/Physics2D/ScriptBindings/Physics2D.bindings.cs b/Modules/Physics2D/ScriptBindings/Physics2D.bindings.cs index 0c78d78b28..2cc5fe5c42 100644 --- a/Modules/Physics2D/ScriptBindings/Physics2D.bindings.cs +++ b/Modules/Physics2D/ScriptBindings/Physics2D.bindings.cs @@ -2456,24 +2456,7 @@ public ContactFilter2D NoFilter() return this; } - private void CheckConsistency() - { - // Clamp depth-range bounds specified as +- infinity to real values. - minDepth = (minDepth == -Mathf.Infinity || minDepth == Mathf.Infinity || Single.IsNaN(minDepth)) ? Single.MinValue : minDepth; - maxDepth = (maxDepth == -Mathf.Infinity || maxDepth == Mathf.Infinity || Single.IsNaN(maxDepth)) ? Single.MaxValue : maxDepth; - if (minDepth > maxDepth) - { - var temp = minDepth; minDepth = maxDepth; maxDepth = temp; - } - - // Clamp normal-range bounds specified as +- infinity to real values. - minNormalAngle = Single.IsNaN(minNormalAngle) ? 0.0f : Mathf.Clamp(minNormalAngle, 0.0f, NormalAngleUpperLimit); - maxNormalAngle = Single.IsNaN(maxNormalAngle) ? NormalAngleUpperLimit : Mathf.Clamp(maxNormalAngle, 0.0f, NormalAngleUpperLimit); - if (minNormalAngle > maxNormalAngle) - { - var temp = minNormalAngle; minNormalAngle = maxNormalAngle; maxNormalAngle = temp; - } - } + extern private void CheckConsistency(); public void ClearLayerMask() { useLayerMask = false; } public void SetLayerMask(LayerMask layerMask) { this.layerMask = layerMask; useLayerMask = true; } @@ -2519,29 +2502,15 @@ public bool IsFilteringDepth(GameObject obj) return result; } - public bool IsFilteringNormalAngle(Vector2 normal) - { - var angle = Mathf.Atan2(normal.y, normal.x) * Mathf.Rad2Deg; - return IsFilteringNormalAngle(angle); - } + extern public bool IsFilteringNormalAngle(Vector2 normal); public bool IsFilteringNormalAngle(float angle) { - angle -= Mathf.Floor(angle / NormalAngleUpperLimit) * NormalAngleUpperLimit; - var minRange = Mathf.Clamp(minNormalAngle, 0.0f, NormalAngleUpperLimit); - var maxRange = Mathf.Clamp(maxNormalAngle, 0.0f, NormalAngleUpperLimit); - if (minRange > maxRange) - { - var temp = minRange; minRange = maxRange; maxRange = temp; - } - - var result = angle maxRange; - if (useOutsideNormalAngle) - return !result; - - return result; + return IsFilteringNormalAngleUsingAngle(angle); } + extern private bool IsFilteringNormalAngleUsingAngle(float angle); + [NativeName("m_UseTriggers")] public bool useTriggers; [NativeName("m_UseLayerMask")] diff --git a/Modules/ShortcutManagerEditor/ConflictResolverWindow.cs b/Modules/ShortcutManagerEditor/ConflictResolverWindow.cs index eba069033a..ef6859c50f 100644 --- a/Modules/ShortcutManagerEditor/ConflictResolverWindow.cs +++ b/Modules/ShortcutManagerEditor/ConflictResolverWindow.cs @@ -258,7 +258,7 @@ private void OnDisable() //We need to delay this action, since actions can depend on the right view having focus, and when closing a window //that will change the current focused view to null. - new DelayedCallback(() => { + EditorApplication.CallDelayed(() => { m_PreviouslyFocusedView?.Focus(); switch (m_CloseBehaviour) diff --git a/Modules/TerrainEditor/TerrainInspector.cs b/Modules/TerrainEditor/TerrainInspector.cs index c44ee21b4d..1536511533 100644 --- a/Modules/TerrainEditor/TerrainInspector.cs +++ b/Modules/TerrainEditor/TerrainInspector.cs @@ -2357,10 +2357,11 @@ public void OnSceneGUICallback(SceneView sceneView) } int id = GUIUtility.GetControlID(s_TerrainEditorHash, FocusType.Passive); + var eventType = e.GetTypeForControl(id); if (!hitValidTerrain) { // if we release the mouse button outside the terrain we still need to update the terrains. ( case 1089947 ) - if (e.GetTypeForControl(id) == EventType.MouseUp) + if (eventType == EventType.MouseUp) PaintContext.ApplyDelayedActions(); return; @@ -2370,7 +2371,7 @@ public void OnSceneGUICallback(SceneView sceneView) bool changeSelection = false; - switch (e.GetTypeForControl(id)) + switch (eventType) { case EventType.Layout: if (!IsModificationToolActive()) @@ -2391,11 +2392,11 @@ public void OnSceneGUICallback(SceneView sceneView) return; // Don't do anything on MouseDrag if we don't own the hotControl. - if (e.GetTypeForControl(id) == EventType.MouseDrag && EditorGUIUtility.hotControl != id) + if (eventType == EventType.MouseDrag && EditorGUIUtility.hotControl != id) return; // If user is ALT-dragging, we want to return to main routine - if (Event.current.alt) + if (e.alt) return; // Allow painting with LMB only @@ -2405,6 +2406,7 @@ public void OnSceneGUICallback(SceneView sceneView) if (!IsModificationToolActive()) return; + HandleUtility.AddDefaultControl(id); if (HandleUtility.nearestControl != id) return; diff --git a/Modules/UIElements/EventDispatcher.cs b/Modules/UIElements/EventDispatcher.cs index 23df931be3..8152b6d0cd 100644 --- a/Modules/UIElements/EventDispatcher.cs +++ b/Modules/UIElements/EventDispatcher.cs @@ -169,6 +169,10 @@ internal void Dispatch(EventBase evt, IPanel panel, DispatchMode dispatchMode) internal void PushDispatcherContext() { + // Drain the event queue before pushing a new context. This allows some important events + // (such as AttachToPanel events) to be processed before showing a modal window. (Fixes case 1215148). + ProcessEventQueue(); + m_DispatchContexts.Push(new DispatchContext() {m_GateCount = m_GateCount, m_Queue = m_Queue}); m_GateCount = 0; m_Queue = k_EventQueuePool.Get(); diff --git a/Modules/UIElements/Events/MouseEventDispatchingStrategy.cs b/Modules/UIElements/Events/MouseEventDispatchingStrategy.cs index 3caeff5f32..e525455fee 100644 --- a/Modules/UIElements/Events/MouseEventDispatchingStrategy.cs +++ b/Modules/UIElements/Events/MouseEventDispatchingStrategy.cs @@ -2,6 +2,8 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using UnityEngine.Assertions; + namespace UnityEngine.UIElements { class MouseEventDispatchingStrategy : IEventDispatchingStrategy @@ -11,54 +13,64 @@ public bool CanDispatchEvent(EventBase evt) return evt is IMouseEvent; } - public void DispatchEvent(EventBase evt, IPanel panel) + public void DispatchEvent(EventBase evt, IPanel iPanel) { - SetBestTargetForEvent(evt, panel); - SendEventToTarget(evt, panel); + if (iPanel != null) + { + Assert.IsTrue(iPanel is BaseVisualElementPanel); + var panel = (BaseVisualElementPanel)iPanel; + SetBestTargetForEvent(evt, panel); + SendEventToTarget(evt, panel); + } evt.stopDispatch = true; } - static void SendEventToTarget(EventBase evt, IPanel panel) + static bool SendEventToTarget(EventBase evt, BaseVisualElementPanel panel) { - SendEventToRegularTarget(evt, panel); - - if (evt.imguiEvent?.rawType == EventType.Used) - evt.StopPropagation(); - - if (evt.isPropagationStopped) - return; - - SendEventToIMGUIContainer(evt, panel); + return SendEventToRegularTarget(evt, panel) || + SendEventToIMGUIContainer(evt, panel); } - static void SendEventToRegularTarget(EventBase evt, IPanel panel) + static bool SendEventToRegularTarget(EventBase evt, BaseVisualElementPanel panel) { - if (evt.target != null) + if (evt.target == null) + return false; + + EventDispatchUtilities.PropagateEvent(evt); + if (evt.target is IMGUIContainer) { - EventDispatchUtilities.PropagateEvent(evt); + evt.propagateToIMGUI = true; + evt.skipElements.Add(evt.target); } + + return IsDone(evt); } - static void SendEventToIMGUIContainer(EventBase evt, IPanel panel) + static bool SendEventToIMGUIContainer(EventBase evt, BaseVisualElementPanel panel) { - if (panel != null) + if (evt.propagateToIMGUI || + evt.eventTypeId == MouseEnterWindowEvent.TypeId() || + evt.eventTypeId == MouseLeaveWindowEvent.TypeId() + ) { - if (evt.target != null && evt.target is IMGUIContainer) - { - evt.propagateToIMGUI = true; - evt.skipElements.Add(evt.target); - } - if (evt.propagateToIMGUI || - evt.eventTypeId == MouseEnterWindowEvent.TypeId() || - evt.eventTypeId == MouseLeaveWindowEvent.TypeId() - ) + EventDispatchUtilities.PropagateToIMGUIContainer(panel.visualTree, evt); + } + else + { + // Send the events to the GUIView container so that it can process them. + // This is necessary for some behaviors like dropdown menus in IMGUI. + // See case : https://fogbugz.unity3d.com/f/cases/1223087/ + var topLevelIMGUI = panel.rootIMGUIContainer; + if (topLevelIMGUI != null && !evt.Skip(topLevelIMGUI) && evt.imguiEvent != null) { - EventDispatchUtilities.PropagateToIMGUIContainer(panel.visualTree, evt); + topLevelIMGUI.SendEventToIMGUI(evt, false); } } + + return IsDone(evt); } - static void SetBestTargetForEvent(EventBase evt, IPanel panel) + static void SetBestTargetForEvent(EventBase evt, BaseVisualElementPanel panel) { UpdateElementUnderMouse(evt, panel, out VisualElement elementUnderMouse); @@ -80,39 +92,35 @@ static void SetBestTargetForEvent(EventBase evt, IPanel panel) } } - static void UpdateElementUnderMouse(EventBase evt, IPanel panel, out VisualElement elementUnderMouse) + static void UpdateElementUnderMouse(EventBase evt, BaseVisualElementPanel panel, out VisualElement elementUnderMouse) { - IMouseEvent mouseEvent = evt as IMouseEvent; - BaseVisualElementPanel basePanel = panel as BaseVisualElementPanel; - - bool shouldRecomputeTopElementUnderMouse = true; - if ((IMouseEventInternal)mouseEvent != null) - { - shouldRecomputeTopElementUnderMouse = - ((IMouseEventInternal)mouseEvent).recomputeTopElementUnderMouse; - } + bool shouldRecomputeTopElementUnderMouse = (evt as IMouseEventInternal)?.recomputeTopElementUnderMouse ?? true; elementUnderMouse = shouldRecomputeTopElementUnderMouse - ? basePanel?.Pick(mouseEvent.mousePosition) - : basePanel?.GetTopElementUnderPointer(PointerId.mousePointerId); + ? panel.Pick(((IMouseEvent)evt).mousePosition) + : panel.GetTopElementUnderPointer(PointerId.mousePointerId); - if (basePanel != null) + // If mouse leaves the window, make sure element under mouse is null. + // However, if pressed button != 0, we are getting a MouseLeaveWindowEvent as part of + // of a drag and drop operation, at the very beginning of the drag. Since + // we are not really exiting the window, we do not want to set the element + // under mouse to null in this case. + if (evt.eventTypeId == MouseLeaveWindowEvent.TypeId() && + (evt as MouseLeaveWindowEvent).pressedButtons == 0) { - // If mouse leaves the window, make sure element under mouse is null. - // However, if pressed button != 0, we are getting a MouseLeaveWindowEvent as part of - // of a drag and drop operation, at the very beginning of the drag. Since - // we are not really exiting the window, we do not want to set the element - // under mouse to null in this case. - if (evt.eventTypeId == MouseLeaveWindowEvent.TypeId() && - (evt as MouseLeaveWindowEvent).pressedButtons == 0) - { - basePanel.SetElementUnderPointer(null, evt); - } - else if (shouldRecomputeTopElementUnderMouse) - { - basePanel.SetElementUnderPointer(elementUnderMouse, evt); - } + panel.SetElementUnderPointer(null, evt); } + else if (shouldRecomputeTopElementUnderMouse) + { + panel.SetElementUnderPointer(elementUnderMouse, evt); + } + } + + static bool IsDone(EventBase evt) + { + if (evt.imguiEvent?.rawType == EventType.Used) + evt.StopPropagation(); + return evt.isPropagationStopped; } } } diff --git a/Modules/UIElements/IMGUIContainer.cs b/Modules/UIElements/IMGUIContainer.cs index 23e1e483a3..b5e114f340 100644 --- a/Modules/UIElements/IMGUIContainer.cs +++ b/Modules/UIElements/IMGUIContainer.cs @@ -588,14 +588,6 @@ internal bool HandleIMGUIEvent(Event e, Action onGUIHandler, bool canAffectFocus return HandleIMGUIEvent(e, m_CachedTransform, m_CachedClippingRect, onGUIHandler, canAffectFocus); } - private bool IsIMGUILayoutPassRequired(Event e, bool wantsMouseMove) - { - return m_RefreshCachedLayout || e.rawType == EventType.Repaint - // We are handling these event types because of some legacy IMGUI editor window doing funky stuff in the layout pass. - || e.rawType == EventType.MouseUp || (wantsMouseMove && e.rawType == EventType.MouseDown) - || e.rawType == EventType.ExecuteCommand || e.rawType == EventType.ValidateCommand; - } - private bool HandleIMGUIEvent(Event e, Matrix4x4 worldTransform, Rect clippingRect, Action onGUIHandler, bool canAffectFocus) { if (e == null || onGUIHandler == null || elementPanel == null || elementPanel.IMGUIEventInterests.WantsEvent(e.rawType) == false) @@ -606,7 +598,7 @@ private bool HandleIMGUIEvent(Event e, Matrix4x4 worldTransform, Rect clippingRe EventType originalEventType = e.rawType; if (originalEventType != EventType.Layout) { - if (IsIMGUILayoutPassRequired(e, elementPanel.IMGUIEventInterests.wantsMouseMove)) + if (m_RefreshCachedLayout || elementPanel.IMGUIEventInterests.WantsLayoutPass(e.rawType)) { // Only update the layout in-between repaint events. e.type = EventType.Layout; diff --git a/Modules/UIElements/Panel.cs b/Modules/UIElements/Panel.cs index 55bb66b36f..947cc2845b 100644 --- a/Modules/UIElements/Panel.cs +++ b/Modules/UIElements/Panel.cs @@ -140,6 +140,7 @@ abstract class BaseVisualElementPanel : IPanel public abstract GetViewDataDictionary getViewDataDictionary { get; set; } public abstract int IMGUIContainersCount { get; set; } public abstract FocusController focusController { get; set; } + public abstract IMGUIContainer rootIMGUIContainer { get; set; } protected BaseVisualElementPanel() { @@ -465,6 +466,8 @@ void CreateMarkers() public override int IMGUIContainersCount { get; set; } + public override IMGUIContainer rootIMGUIContainer { get; set; } + internal override uint version { get { return m_Version; } diff --git a/Modules/UIElements/Renderer/UIRChainBuilderImpl.cs b/Modules/UIElements/Renderer/UIRChainBuilderImpl.cs index 20d2cb7171..54dba9e561 100644 --- a/Modules/UIElements/Renderer/UIRChainBuilderImpl.cs +++ b/Modules/UIElements/Renderer/UIRChainBuilderImpl.cs @@ -83,6 +83,7 @@ static Vector4 GetClipRectIDClipInfo(VisualElement ve) var transform = ve.renderChainData.groupTransformAncestor.worldTransform.inverse; var min = transform.MultiplyPoint3x4(new Vector3(rect.xMin, rect.yMin, 0)); var max = transform.MultiplyPoint3x4(new Vector3(rect.xMax, rect.yMax, 0)); + return new Vector4(Mathf.Min(min.x, max.x), Mathf.Min(min.y, max.y), Mathf.Max(min.x, max.x), Mathf.Max(min.y, max.y)); } @@ -756,6 +757,7 @@ internal static UIRStylePainter PaintElement(RenderChain renderChain, VisualElem // Copy vertices, transforming them as necessary var targetVerticesSlice = verts.Slice(vertsFilled, entry.vertices.Length); + if (entry.uvIsDisplacement) { if (firstDisplacementUV < 0) @@ -1993,12 +1995,17 @@ public void Begin(VisualElement ve, UIRenderDevice device) var oldVertexAlloc = ve.renderChainData.data.allocVerts; var oldVertexData = ve.renderChainData.data.allocPage.vertices.cpuData.Slice((int)oldVertexAlloc.start, (int)oldVertexAlloc.size); device.Update(ve.renderChainData.data, ve.renderChainData.data.allocVerts.size, out m_MeshDataVerts); - if (ve.renderChainData.textEntries.Count > 1 || ve.renderChainData.textEntries[0].vertexCount != m_MeshDataVerts.Length) + RenderChainTextEntry firstTextEntry = ve.renderChainData.textEntries[0]; + if (ve.renderChainData.textEntries.Count > 1 || firstTextEntry.vertexCount != m_MeshDataVerts.Length) m_MeshDataVerts.CopyFrom(oldVertexData); // Preserve old data because we're not just updating the text vertices, but the entire mesh surrounding it though we won't touch but the text vertices - m_XFormClipPages = oldVertexData[0].xformClipPages; - m_IDsFlags = oldVertexData[0].idsFlags; - m_OpacityPagesSettingsIndex = oldVertexData[0].opacityPageSVGSettingIndex; + // Case 1222517: Background and border are clipped by the parent, which implies that they may have a + // different clip id when compared to the content, if overflow-clip-box is set to content-box. As a result, + // we must NOT use the "first vertex" but rather the "first vertex of the first text entry". + int first = firstTextEntry.firstVertex; + m_XFormClipPages = oldVertexData[first].xformClipPages; + m_IDsFlags = oldVertexData[first].idsFlags; + m_OpacityPagesSettingsIndex = oldVertexData[first].opacityPageSVGSettingIndex; } public void End() diff --git a/Modules/UIElements/VisualElement.cs b/Modules/UIElements/VisualElement.cs index 084570dc78..fe2c05a438 100644 --- a/Modules/UIElements/VisualElement.cs +++ b/Modules/UIElements/VisualElement.cs @@ -654,7 +654,10 @@ private void UpdateWorldClip() if (ShouldClip()) { - var wb = worldBound; + // Case 1222517: We must substract before intersection. Otherwise, if the parent world clip + // boundary happens to be overlapping the element, we may be over-substracting. Also clamping must + // be the last operation that's performed. + Rect wb = SubstractBorderPadding(worldBound); float x1 = Mathf.Max(wb.xMin, m_WorldClip.xMin); float x2 = Mathf.Min(wb.xMax, m_WorldClip.xMax); @@ -662,7 +665,7 @@ private void UpdateWorldClip() float y2 = Mathf.Min(wb.yMax, m_WorldClip.yMax); float width = Mathf.Max(x2 - x1, 0); float height = Mathf.Max(y2 - y1, 0); - m_WorldClip = SubstractBorderPadding(new Rect(x1, y1, width, height)); + m_WorldClip = new Rect(x1, y1, width, height); x1 = Mathf.Max(wb.xMin, m_WorldClipMinusGroup.xMin); x2 = Mathf.Min(wb.xMax, m_WorldClipMinusGroup.xMax); @@ -670,7 +673,7 @@ private void UpdateWorldClip() y2 = Mathf.Min(wb.yMax, m_WorldClipMinusGroup.yMax); width = Mathf.Max(x2 - x1, 0); height = Mathf.Max(y2 - y1, 0); - m_WorldClipMinusGroup = SubstractBorderPadding(new Rect(x1, y1, width, height)); + m_WorldClipMinusGroup = new Rect(x1, y1, width, height); } } else @@ -679,22 +682,26 @@ private void UpdateWorldClip() } } - private Rect SubstractBorderPadding(Rect rect) + private Rect SubstractBorderPadding(Rect worldRect) { - rect.x += resolvedStyle.borderLeftWidth; - rect.y += resolvedStyle.borderTopWidth; - rect.width -= resolvedStyle.borderLeftWidth + resolvedStyle.borderRightWidth; - rect.height -= resolvedStyle.borderTopWidth + resolvedStyle.borderBottomWidth; + // Case 1222517: We must take the scaling into consideration when applying local changes to the world rect. + float xScale = worldTransform.m00; + float yScale = worldTransform.m11; + + worldRect.x += resolvedStyle.borderLeftWidth * xScale; + worldRect.y += resolvedStyle.borderTopWidth * yScale; + worldRect.width -= (resolvedStyle.borderLeftWidth + resolvedStyle.borderRightWidth) * xScale; + worldRect.height -= (resolvedStyle.borderTopWidth + resolvedStyle.borderBottomWidth) * yScale; if (computedStyle.unityOverflowClipBox == OverflowClipBox.ContentBox) { - rect.x += resolvedStyle.paddingLeft; - rect.y += resolvedStyle.paddingTop; - rect.width -= resolvedStyle.paddingLeft + resolvedStyle.paddingRight; - rect.height -= resolvedStyle.paddingTop + resolvedStyle.paddingBottom; + worldRect.x += resolvedStyle.paddingLeft * xScale; + worldRect.y += resolvedStyle.paddingTop * yScale; + worldRect.width -= (resolvedStyle.paddingLeft + resolvedStyle.paddingRight) * xScale; + worldRect.height -= (resolvedStyle.paddingTop + resolvedStyle.paddingBottom) * yScale; } - return rect; + return worldRect; } // get the AA aligned bound diff --git a/Modules/UnityConnectEditor/Services/PurchasingConfiguration.cs b/Modules/UnityConnectEditor/Services/PurchasingConfiguration.cs index 15bc8d12e7..caa0d2acf2 100644 --- a/Modules/UnityConnectEditor/Services/PurchasingConfiguration.cs +++ b/Modules/UnityConnectEditor/Services/PurchasingConfiguration.cs @@ -12,6 +12,7 @@ internal class PurchasingConfiguration static readonly PurchasingConfiguration k_Instance; readonly string m_PurchasingPackageUrl; + readonly string m_AnalyticsApiUrl; readonly string m_GooglePlayDevConsoleUrl; static PurchasingConfiguration() @@ -22,6 +23,7 @@ static PurchasingConfiguration() PurchasingConfiguration() { m_PurchasingPackageUrl = "https://public-cdn.cloud.unity3d.com/UnityEngine.Cloud.Purchasing.unitypackage"; + m_AnalyticsApiUrl = "https://analytics.cloud.unity3d.com"; m_GooglePlayDevConsoleUrl = "https://play.google.com/apps/publish/"; } @@ -32,6 +34,11 @@ public string purchasingPackageUrl get { return m_PurchasingPackageUrl; } } + public string analyticsApiUrl + { + get { return m_AnalyticsApiUrl; } + } + public string googlePlayDevConsoleUrl { get { return m_GooglePlayDevConsoleUrl; } diff --git a/Modules/UnityConnectEditor/Services/PurchasingService.cs b/Modules/UnityConnectEditor/Services/PurchasingService.cs index 87308a18b3..d29a0e1dda 100644 --- a/Modules/UnityConnectEditor/Services/PurchasingService.cs +++ b/Modules/UnityConnectEditor/Services/PurchasingService.cs @@ -283,7 +283,7 @@ public void SubmitGooglePlayKey(string submittedKey, Action onSu private string GetGoogleKeyResource() { - return PurchasingConfiguration.instance.googlePlayDevConsoleUrl + k_GoogleKeySubPath + UnityConnect.instance.projectInfo.projectGUID; + return PurchasingConfiguration.instance.analyticsApiUrl + k_GoogleKeySubPath + UnityConnect.instance.projectInfo.projectGUID; } } } diff --git a/Modules/VirtualTexturing/ScriptBindings/VirtualTexturing.bindings.cs b/Modules/VirtualTexturing/ScriptBindings/VirtualTexturing.bindings.cs index a50d609616..92fa6a47ff 100644 --- a/Modules/VirtualTexturing/ScriptBindings/VirtualTexturing.bindings.cs +++ b/Modules/VirtualTexturing/ScriptBindings/VirtualTexturing.bindings.cs @@ -22,7 +22,11 @@ public static class System extern public static void Update(); public const int AllMips = int.MaxValue; + + [NativeThrows] extern public static void RequestRegion([NotNull] Material mat, int stackNameId, Rect r, int mipMap, int numMips); + [NativeThrows] + extern public static void GetTextureStackSize([NotNull] Material mat, int stackNameId, out int width, out int height); // Apply the virtualtexturing settings to the renderer. This may be an expensive operation so it should be done very sparingly (e.g. during a level load/startup). [NativeThrows] @@ -39,8 +43,6 @@ public static class EditorHelpers [NativeThrows] extern public static bool ValidateTextureStack([NotNull] Texture[] textures, out string errorMessage); - extern internal static bool GetTextureStackSize([NotNull] Material mat, int stackNameId, out int width, out int height); - [NativeConditional("UNITY_EDITOR", "{}")] extern public static GraphicsFormat[] QuerySupportedFormats(); } diff --git a/Projects/CSharp/UnityEditor.csproj b/Projects/CSharp/UnityEditor.csproj index 4631872436..fe8e7b72dd 100644 --- a/Projects/CSharp/UnityEditor.csproj +++ b/Projects/CSharp/UnityEditor.csproj @@ -1072,9 +1072,6 @@ Editor\Mono\GI\LightingDataAsset.bindings.cs - - Editor\Mono\GI\LightingSettings.bindings.cs - Editor\Mono\GI\LightmapEditorSettings.bindings.cs @@ -3310,6 +3307,9 @@ Editor\Mono\UIElements\Controls\BindingExtensions.cs + + Editor\Mono\UIElements\Controls\BindingsInterface.cs + Editor\Mono\UIElements\Controls\BoundsField.cs diff --git a/Projects/CSharp/UnityEngine.csproj b/Projects/CSharp/UnityEngine.csproj index b1f0e94f4d..0b97aada07 100644 --- a/Projects/CSharp/UnityEngine.csproj +++ b/Projects/CSharp/UnityEngine.csproj @@ -139,11 +139,8 @@ Modules\AI\Public\NavMeshBuildSettings.bindings.cs - - Modules\AR\ScriptBindings\ARBackgroundRenderer.cs - - - Modules\AR\Tango\ScriptBindings\Tango.bindings.cs + + Modules\AR\ARCore\ScriptBindings\ARCore.bindings.cs Modules\Accessibility\VisionUtility.cs @@ -2026,6 +2023,9 @@ Runtime\Export\GI\GIDebugVisualisation.bindings.cs + + Runtime\Export\GI\LightingSettings.bindings.cs + Runtime\Export\GI\Lightmapping.cs diff --git a/README.md b/README.md index de6730c7b4..7d34dd2f17 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Unity 2020.1.0a24 C# reference source code +## Unity 2020.1.0b3 C# reference source code The C# part of the Unity engine and editor source code. May be used for reference purposes only. diff --git a/Editor/Mono/GI/LightingSettings.bindings.cs b/Runtime/Export/GI/LightingSettings.bindings.cs similarity index 77% rename from Editor/Mono/GI/LightingSettings.bindings.cs rename to Runtime/Export/GI/LightingSettings.bindings.cs index 299ef6eed6..309ef88aad 100644 --- a/Editor/Mono/GI/LightingSettings.bindings.cs +++ b/Runtime/Export/GI/LightingSettings.bindings.cs @@ -2,15 +2,18 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License -using UnityEngine.Bindings; using UnityEngine; +using UnityEngine.Bindings; +using UnityEngine.Scripting; -namespace UnityEditor +namespace UnityEngine { [NativeHeader("Runtime/Graphics/LightingSettings.h")] - [NativeClass(null)] public sealed partial class LightingSettings : Object { + [RequiredByNativeCode] + internal void LightingSettingsDontStripMe() {} + public LightingSettings() { Internal_Create(this); @@ -18,16 +21,16 @@ public LightingSettings() private extern static void Internal_Create([Writable] LightingSettings self); - internal void CreateAsset() - { - if (string.IsNullOrEmpty(name)) - { - name = "New Lighting Settings"; - } + [NativeName("EnableBakedLightmaps")] + public extern bool bakedGI { get; set; } - ProjectWindowUtil.CreateAsset(this, (name + ".lighting")); - } + [NativeName("EnableRealtimeLightmaps")] + public extern bool realtimeGI { get; set; } + [NativeName("RealtimeEnvironmentLighting")] + public extern bool realtimeEnvironmentLighting { get; set; } + + #region Editor Only // Which baking backend is used. public enum Lightmapper { @@ -93,185 +96,234 @@ public enum FilterType None = 2 } + // This is only here due to issues with giWorkflowMode being in UnityEngine. + // Lightmapping.GIWorkflowMode should be used in the rest of the code + [NativeHeader("Runtime/Graphics/LightmapEnums.h")] + internal enum GIWorkflowMode + { + Iterative = 0, + OnDemand = 1, + Legacy = 2 + } + [NativeName("GIWorkflowMode")] - internal extern Lightmapping.GIWorkflowMode giWorkflowMode { get; set; } + internal extern GIWorkflowMode giWorkflowMode { get; set; } + [NativeConditional("UNITY_EDITOR")] public bool autoGenerate { - get { return giWorkflowMode == Lightmapping.GIWorkflowMode.Iterative; } - set { giWorkflowMode = (value ? Lightmapping.GIWorkflowMode.Iterative : Lightmapping.GIWorkflowMode.OnDemand); } + get { return giWorkflowMode == GIWorkflowMode.Iterative; } + set { giWorkflowMode = (value ? GIWorkflowMode.Iterative : GIWorkflowMode.OnDemand); } } - [NativeName("EnableBakedLightmaps")] - public extern bool bakedGI { get; set; } - - [NativeName("EnableRealtimeLightmaps")] - public extern bool realtimeGI { get; set; } - - [NativeName("RealtimeEnvironmentLighting")] - public extern bool realtimeEnvironmentLighting { get; set; } + [NativeName("MixedBakeMode")] + [NativeConditional("UNITY_EDITOR")] + public extern MixedLightingMode mixedBakeMode { get; set; } [NativeName("AlbedoBoost")] + [NativeConditional("UNITY_EDITOR")] public extern float albedoBoost { get; set; } [NativeName("IndirectOutputScale")] + [NativeConditional("UNITY_EDITOR")] public extern float indirectScale { get; set; } [NativeName("BakeBackend")] + [NativeConditional("UNITY_EDITOR")] public extern Lightmapper lightmapper { get; set; } // The maximum size of an individual lightmap texture. [NativeName("LightmapMaxSize")] + [NativeConditional("UNITY_EDITOR")] public extern int lightmapMaxSize { get; set; } // Static lightmap resolution in texels per world unit. [NativeName("BakeResolution")] + [NativeConditional("UNITY_EDITOR")] public extern float lightmapResolution { get; set; } // Texel separation between shapes. [NativeName("Padding")] + [NativeConditional("UNITY_EDITOR")] public extern int lightmapPadding { get; set; } // Whether to use DXT1 compression on the generated lightmaps. [NativeName("TextureCompression")] + [NativeConditional("UNITY_EDITOR")] public extern bool compressLightmaps { get; set; } // Whether to apply ambient occlusion to the lightmap. [NativeName("AO")] + [NativeConditional("UNITY_EDITOR")] public extern bool ao { get; set; } // Beyond this distance a ray is considered to be un-occluded. [NativeName("AOMaxDistance")] + [NativeConditional("UNITY_EDITOR")] public extern float aoMaxDistance { get; set; } // Exponent for ambient occlusion on indirect lighting. [NativeName("CompAOExponent")] + [NativeConditional("UNITY_EDITOR")] public extern float aoExponentIndirect { get; set; } // Exponent for ambient occlusion on direct lighting. [NativeName("CompAOExponentDirect")] + [NativeConditional("UNITY_EDITOR")] public extern float aoExponentDirect { get; set; } // If we should write out AO to disk. Only works in On Demand bakes [NativeName("ExtractAO")] + [NativeConditional("UNITY_EDITOR")] public extern bool extractAO { get; set; } - [NativeName("MixedBakeMode")] - public extern MixedLightingMode mixedBakeMode { get; set; } - [NativeName("LightmapsBakeMode")] + [NativeConditional("UNITY_EDITOR")] public extern LightmapsMode directionalityMode { get; set; } [NativeName("FilterMode")] + [NativeConditional("UNITY_EDITOR")] internal extern UnityEngine.FilterMode lightmapFilterMode { get; set; } + [NativeConditional("UNITY_EDITOR")] public extern bool exportTrainingData { get; set; } + [NativeConditional("UNITY_EDITOR")] public extern string trainingDataDestination { get; set; } // Realtime lightmap resolution in texels per world unit. Also used for indirect resolution when using baked GI. [NativeName("RealtimeResolution")] + [NativeConditional("UNITY_EDITOR")] public extern float indirectResolution { get; set; } [NativeName("ForceWhiteAlbedo")] + [NativeConditional("UNITY_EDITOR")] internal extern bool realtimeForceWhiteAlbedo { get; set; } [NativeName("ForceUpdates")] + [NativeConditional("UNITY_EDITOR")] internal extern bool realtimeForceUpdates { get; set; } + [NativeConditional("UNITY_EDITOR")] internal extern bool finalGather { get; set; } + [NativeConditional("UNITY_EDITOR")] internal extern float finalGatherRayCount { get; set; } + [NativeConditional("UNITY_EDITOR")] internal extern bool finalGatherFiltering { get; set; } [NativeName("PVRSampling")] + [NativeConditional("UNITY_EDITOR")] public extern Sampling sampling { get; set; } [NativeName("PVRDirectSampleCount")] + [NativeConditional("UNITY_EDITOR")] public extern int directSampleCount { get; set; } [NativeName("PVRSampleCount")] + [NativeConditional("UNITY_EDITOR")] public extern int indirectSampleCount { get; set; } // Amount of light bounce used for the path tracer. [NativeName("PVRBounces")] + [NativeConditional("UNITY_EDITOR")] public extern int bounces { get; set; } // Choose at which bounce we start to apply russian roulette to the ray [NativeName("PVRRussianRouletteStartBounce")] + [NativeConditional("UNITY_EDITOR")] public extern int russianRouletteStartBounce { get; set; } // Is view prioritisation enabled? [NativeName("PVRCulling")] + [NativeConditional("UNITY_EDITOR")] public extern bool prioritizeView { get; set; } // Which path tracer filtering mode is used. [NativeName("PVRFilteringMode")] + [NativeConditional("UNITY_EDITOR")] public extern FilterMode filteringMode { get; set; } // Which path tracer denoiser is used for the direct light. [NativeName("PVRDenoiserTypeDirect")] + [NativeConditional("UNITY_EDITOR")] public extern DenoiserType denoiserTypeDirect { get; set; } // Which path tracer denoiser is used for the indirect light. [NativeName("PVRDenoiserTypeIndirect")] + [NativeConditional("UNITY_EDITOR")] public extern DenoiserType denoiserTypeIndirect { get; set; } // Which path tracer denoiser is used for ambient occlusion. [NativeName("PVRDenoiserTypeAO")] + [NativeConditional("UNITY_EDITOR")] public extern DenoiserType denoiserTypeAO { get; set; } // Which path tracer filter is used for the direct light. [NativeName("PVRFilterTypeDirect")] + [NativeConditional("UNITY_EDITOR")] public extern FilterType filterTypeDirect { get; set; } // Which path tracer filter is used for the indirect light. [NativeName("PVRFilterTypeIndirect")] + [NativeConditional("UNITY_EDITOR")] public extern FilterType filterTypeIndirect { get; set; } // Which path tracer filter is used for ambient occlusion. [NativeName("PVRFilterTypeAO")] + [NativeConditional("UNITY_EDITOR")] public extern FilterType filterTypeAO { get; set; } // Which radius is used for the direct light path tracer filter if gauss is chosen. [NativeName("PVRFilteringGaussRadiusDirect")] + [NativeConditional("UNITY_EDITOR")] public extern int filteringGaussRadiusDirect { get; set; } // Which radius is used for the indirect light path tracer filter if gauss is chosen. [NativeName("PVRFilteringGaussRadiusIndirect")] + [NativeConditional("UNITY_EDITOR")] public extern int filteringGaussRadiusIndirect { get; set; } // Which radius is used for AO path tracer filter if gauss is chosen. [NativeName("PVRFilteringGaussRadiusAO")] + [NativeConditional("UNITY_EDITOR")] public extern int filteringGaussRadiusAO { get; set; } // Which position sigma is used for the direct light path tracer filter if Atrous is chosen. [NativeName("PVRFilteringAtrousPositionSigmaDirect")] + [NativeConditional("UNITY_EDITOR")] public extern float filteringAtrousPositionSigmaDirect { get; set; } // Which position sigma is used for the indirect light path tracer filter if Atrous is chosen. [NativeName("PVRFilteringAtrousPositionSigmaIndirect")] + [NativeConditional("UNITY_EDITOR")] public extern float filteringAtrousPositionSigmaIndirect { get; set; } // Which position sigma is used for AO path tracer filter if Atrous is chosen. [NativeName("PVRFilteringAtrousPositionSigmaAO")] + [NativeConditional("UNITY_EDITOR")] public extern float filteringAtrousPositionSigmaAO { get; set; } // Whether to enable or disable environment multiple importance sampling [NativeName("PVREnvironmentMIS")] + [NativeConditional("UNITY_EDITOR")] internal extern int environmentMIS { get; set; } // How many samples to use for environment sampling [NativeName("PVREnvironmentSampleCount")] + [NativeConditional("UNITY_EDITOR")] public extern int environmentSampleCount { get; set; } // How many reference points to generate when using MIS [NativeName("PVREnvironmentReferencePointCount")] + [NativeConditional("UNITY_EDITOR")] internal extern int environmentReferencePointCount { get; set; } // How many samples to use for light probes relative to lightmap texels [NativeName("LightProbeSampleCountMultiplier")] + [NativeConditional("UNITY_EDITOR")] public extern float lightProbeSampleCountMultiplier { get; set; } + #endregion } } diff --git a/Runtime/Export/Jobs/AtomicSafetyHandle.bindings.cs b/Runtime/Export/Jobs/AtomicSafetyHandle.bindings.cs index edb2ea7e8e..22b29a798d 100644 --- a/Runtime/Export/Jobs/AtomicSafetyHandle.bindings.cs +++ b/Runtime/Export/Jobs/AtomicSafetyHandle.bindings.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using System.Text; using System.Diagnostics; using UnityEngine.Bindings; using UnityEngine.Scripting; @@ -181,10 +182,21 @@ public static unsafe void CheckExistsAndThrow(AtomicSafetyHandle handle) public static extern string GetWriterName(AtomicSafetyHandle handle); [ThreadSafe] - public static extern int NewStaticSafetyId(string ownerTypeName); + public static unsafe extern int NewStaticSafetyId(byte* ownerTypeNameBytes, int byteCount); + + public static unsafe int NewStaticSafetyId() + { + var ownerTypeName = typeof(T).ToString(); + var bytes = Encoding.UTF8.GetBytes(ownerTypeName); + fixed(byte* pBytes = bytes) + { + return NewStaticSafetyId(pBytes, bytes.Length); + } + } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] [NativeThrows, ThreadSafe] - public static extern void SetCustomErrorMessage(int staticSafetyId, AtomicSafetyErrorType errorType, string message); + public static unsafe extern void SetCustomErrorMessage(int staticSafetyId, AtomicSafetyErrorType errorType, byte* messageBytes, int byteCount); [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] [NativeThrows, ThreadSafe] public static extern void SetStaticSafetyId(ref AtomicSafetyHandle handle, int staticSafetyId); diff --git a/Runtime/Export/NativeArray/DisposeSentinel.cs b/Runtime/Export/NativeArray/DisposeSentinel.cs index eefa90e776..b00f537ad9 100644 --- a/Runtime/Export/NativeArray/DisposeSentinel.cs +++ b/Runtime/Export/NativeArray/DisposeSentinel.cs @@ -76,7 +76,11 @@ public static void Dispose(ref AtomicSafetyHandle safety, ref DisposeSentinel se // If the safety handle is for a temp allocation, create a new safety handle for this instance which can be marked as invalid // Setting it to new AtomicSafetyHandle is not enough since the handle needs a valid node pointer in order to give the correct errors if (AtomicSafetyHandle.IsTempMemoryHandle(safety)) + { + int staticSafetyId = safety.staticSafetyId; safety = AtomicSafetyHandle.Create(); + safety.staticSafetyId = staticSafetyId; + } AtomicSafetyHandle.Release(safety); Clear(ref sentinel); } diff --git a/Runtime/Export/NativeArray/NativeArray.cs b/Runtime/Export/NativeArray/NativeArray.cs index d2f4be2acc..cfc91bd2d4 100644 --- a/Runtime/Export/NativeArray/NativeArray.cs +++ b/Runtime/Export/NativeArray/NativeArray.cs @@ -38,16 +38,17 @@ public unsafe struct NativeArray : IDisposable, IEnumerable, IEquatable>(); + // and InitStaticSafetyId() can be replaced with a call to AtomicSafetyHandle.SetStaticSafetyId(); + static int s_staticSafetyId; [BurstDiscard] - static void AssignStaticSafetyId(ref AtomicSafetyHandle safetyHandle) + static void InitStaticSafetyId(ref AtomicSafetyHandle handle) { if (s_staticSafetyId == 0) - { - s_staticSafetyId = AtomicSafetyHandle.NewStaticSafetyId($"NativeArray<{typeof(T).Name}>"); - } - AtomicSafetyHandle.SetStaticSafetyId(ref safetyHandle, s_staticSafetyId); + s_staticSafetyId = AtomicSafetyHandle.NewStaticSafetyId>(); + AtomicSafetyHandle.SetStaticSafetyId(ref handle, s_staticSafetyId); } @@ -101,7 +102,7 @@ static void Allocate(int length, Allocator allocator, out NativeArray array) array.m_MinIndex = 0; array.m_MaxIndex = length - 1; DisposeSentinel.Create(out array.m_Safety, out array.m_DisposeSentinel, 1, allocator); - AssignStaticSafetyId(ref array.m_Safety); + InitStaticSafetyId(ref array.m_Safety); } public int Length => m_Length; diff --git a/Runtime/Export/PlayerLoop/PlayerLoop.bindings.cs b/Runtime/Export/PlayerLoop/PlayerLoop.bindings.cs index 409b700919..818c168430 100644 --- a/Runtime/Export/PlayerLoop/PlayerLoop.bindings.cs +++ b/Runtime/Export/PlayerLoop/PlayerLoop.bindings.cs @@ -91,8 +91,11 @@ public struct PhysicsResetInterpolatedTransformPosition {} [RequiredByNativeCode] public struct SpriteAtlasManagerUpdate {} [RequiredByNativeCode] + [Obsolete("TangoUpdate has been deprecated. Use ARCoreUpdate instead (UnityUpgradable) -> UnityEngine.PlayerLoop.EarlyUpdate/ARCoreUpdate", false)] public struct TangoUpdate {} [RequiredByNativeCode] + public struct ARCoreUpdate {} + [RequiredByNativeCode] public struct PerformanceAnalyticsUpdate {} } [RequiredByNativeCode] diff --git a/Runtime/Export/RenderPipeline/SupportedRenderingFeatures.cs b/Runtime/Export/RenderPipeline/SupportedRenderingFeatures.cs index ca8aae0586..262ff5bba5 100644 --- a/Runtime/Export/RenderPipeline/SupportedRenderingFeatures.cs +++ b/Runtime/Export/RenderPipeline/SupportedRenderingFeatures.cs @@ -66,6 +66,8 @@ public enum LightmapMixedBakeModes public bool overridesLODBias { get; set; } = false; public bool overridesMaximumLODLevel { get; set; } = false; public bool rendererProbes { get; set; } = true; + public bool particleSystemInstancing { get; set; } = true; + internal static unsafe MixedLightingMode FallbackMixedLightingMode() { MixedLightingMode fallbackMode; diff --git a/Runtime/Export/Scripting/LazyLoadReference.cs b/Runtime/Export/Scripting/LazyLoadReference.cs index 7fea5e9873..ce8445caeb 100644 --- a/Runtime/Export/Scripting/LazyLoadReference.cs +++ b/Runtime/Export/Scripting/LazyLoadReference.cs @@ -25,8 +25,12 @@ namespace UnityEngine // - *** The memory layout of this struct must be identical to the native type: AssetReferenceMemoryLayout. *** // - Not using bindings file as we don't want a wrapper class in this situation. but it must mirror it's native counter part to a 'T'. //---------------------------------------------------------------------------------------------------------------------- - [StructLayout(LayoutKind.Sequential)] - [Serializable] + + /// + /// Serializable lazy reference to a contained in an asset file, where referenced object is loaded only when accessed and not at deserialization of this struct. + /// + /// The type of the asset. + [Serializable, StructLayout(LayoutKind.Sequential)] public struct LazyLoadReference where T : UnityEngine.Object { private const int kInstanceID_None = 0; @@ -34,48 +38,111 @@ public struct LazyLoadReference where T : UnityEngine.Object [SerializeField] private int m_InstanceID; - public int instanceID - { - get { return m_InstanceID; } - set { m_InstanceID = value; } - } - - // Determines if the reference is linked to an object, loaded or not, valid or not. - // Calling this never triggers a load. - public bool isSet => m_InstanceID == kInstanceID_None; + /// + /// Determines if the reference is linked to an asset, loaded or not, valid or not. + /// Calling this never triggers a load. + /// + public bool isSet => m_InstanceID != kInstanceID_None; - // Convenience property that checks if the reference is broken: is set to something, but that something is not available/loadable at the moment for whatever reason. - // Note that this will may trigger loading the referenced object into memory. + /// + /// Convenience property that checks if the reference is broken: is set to something, but that something is not available/loadable at the moment for whatever reason. + /// May trigger loading the referenced object into memory if the object is not already loaded. + /// public bool isBroken => m_InstanceID != kInstanceID_None && !UnityEngine.Object.DoesObjectWithInstanceIDExist(m_InstanceID); - // Accessor to the referenenced object/asset. - // Note that this will may trigger loading the referenced object into memory. + /// + /// Accessor to the referenced asset. + /// May trigger loading the referenced object into memory if the object is not already loaded. + /// public T asset { get { if (m_InstanceID == kInstanceID_None) + { return null; + } else { return (T)Object.ForceLoadFromInstanceID(m_InstanceID); } } - set { - if (value != null) + if (value == null) + { + m_InstanceID = kInstanceID_None; + } + else { if (!Object.IsPersistent(value)) { throw new ArgumentException("Object that does not belong to a persisted asset cannot be set as the target of a LazyLoadReference."); } - m_InstanceID = value.GetInstanceID(); } - else - m_InstanceID = kInstanceID_None; } } + + /// + /// InstanceID of the referenced asset. + /// Getting or setting this never triggers a load. + /// + public int instanceID + { + get => m_InstanceID; + set => m_InstanceID = value; + } + + /// + /// Construct a from asset reference. + /// May trigger loading the referenced object into memory if the object is not already loaded. + /// + /// + public LazyLoadReference(T asset) + { + if (asset == null) + { + m_InstanceID = kInstanceID_None; + } + else + { + if (!Object.IsPersistent(asset)) + { + throw new ArgumentException("Object that does not belong to a persisted asset cannot be set as the target of a LazyLoadReference."); + } + m_InstanceID = asset.GetInstanceID(); + } + } + + /// + /// Construct a from asset instance ID. + /// Calling this never triggers a load. + /// + /// + public LazyLoadReference(int instanceID) + { + m_InstanceID = instanceID; + } + + /// + /// Implicit conversion from asset to . + /// May trigger loading the referenced object into memory if the object is not already loaded. + /// + /// The asset reference. + public static implicit operator LazyLoadReference(T asset) + { + return new LazyLoadReference { asset = asset }; + } + + /// + /// Implicit conversion from asset instance ID to . + /// Calling this never triggers a load. + /// + /// The asset instance ID. + public static implicit operator LazyLoadReference(int instanceID) + { + return new LazyLoadReference { instanceID = instanceID }; + } } } diff --git a/Runtime/Export/Scripting/UnitySynchronizationContext.cs b/Runtime/Export/Scripting/UnitySynchronizationContext.cs index 26c30119ad..677432fd9c 100644 --- a/Runtime/Export/Scripting/UnitySynchronizationContext.cs +++ b/Runtime/Export/Scripting/UnitySynchronizationContext.cs @@ -79,10 +79,13 @@ private void Exec() m_AsyncWorkQueue.Clear(); } - foreach (var work in m_CurrentFrameWork) + // When you invoke work, remove it from the list to stop it being triggered again (case 1213602) + while (m_CurrentFrameWork.Count > 0) + { + WorkRequest work = m_CurrentFrameWork[0]; + m_CurrentFrameWork.Remove(work); work.Invoke(); - - m_CurrentFrameWork.Clear(); + } } private bool HasPendingTasks() diff --git a/Runtime/Export/Stripping/ClassStubsForStripping.cs b/Runtime/Export/Stripping/ClassStubsForStripping.cs index 06546dc3d1..d0bc540619 100644 --- a/Runtime/Export/Stripping/ClassStubsForStripping.cs +++ b/Runtime/Export/Stripping/ClassStubsForStripping.cs @@ -20,16 +20,3 @@ internal class PreloadData : Object internal void PreloadDataDontStripMe() {} } } - -namespace UnityEngine.LightingSettingsPrivate -{ - // The LightingSettings native class needs to be preserved in player builds, even if no instance of the class is - // present in the game data, as an instance needs to be created in code in that case. But the managed API representation - // of that class only exists in UnityEditor code, so we add a dummy runtime version here to preserve the type - [ExcludeFromObjectFactory] - internal class LightingSettings : Object - { - [RequiredByNativeCode] - internal void LightingSettingsDontStripMe() {} - } -} diff --git a/Runtime/Export/iOS/iOSDevice.bindings.cs b/Runtime/Export/iOS/iOSDevice.bindings.cs index 34b2453048..f3b27acfd8 100644 --- a/Runtime/Export/iOS/iOSDevice.bindings.cs +++ b/Runtime/Export/iOS/iOSDevice.bindings.cs @@ -100,6 +100,10 @@ extern public static string vendorIdentifier get; } + // please note that we check both advertisingIdentifier/advertisingTrackingEnabled + // usage in scripts to decide if we should enable UNITY_USES_IAD macro (i.e. code that uses iAD and related things) + // that's why it is VERY important that you use private extern functions instead of properties in internal/implementation code + [NativeConditional("PLATFORM_IOS || PLATFORM_TVOS")] [FreeFunction("UnityAdvertisingIdentifier")] extern private static string GetAdvertisingIdentifier(); @@ -109,15 +113,21 @@ public static string advertisingIdentifier get { string advertisingId = GetAdvertisingIdentifier(); - Application.InvokeOnAdvertisingIdentifierCallback(advertisingId, advertisingTrackingEnabled); + Application.InvokeOnAdvertisingIdentifierCallback(advertisingId, IsAdvertisingTrackingEnabled()); return advertisingId; } } - extern public static bool advertisingTrackingEnabled + [NativeConditional("PLATFORM_IOS || PLATFORM_TVOS")] + [FreeFunction("IOSScripting::IsAdvertisingTrackingEnabled")] + extern private static bool IsAdvertisingTrackingEnabled(); + + public static bool advertisingTrackingEnabled { - [NativeConditional("PLATFORM_IOS || PLATFORM_TVOS")] - [FreeFunction("IOSScripting::IsAdvertisingTrackingEnabled")] get; + get + { + return IsAdvertisingTrackingEnabled(); + } } extern public static bool hideHomeButton diff --git a/Runtime/Export/iOS/tvOSDevice.bindings.cs b/Runtime/Export/iOS/tvOSDevice.bindings.cs index eca3f3ad58..97a133fc14 100644 --- a/Runtime/Export/iOS/tvOSDevice.bindings.cs +++ b/Runtime/Export/iOS/tvOSDevice.bindings.cs @@ -56,29 +56,31 @@ public static string vendorIdentifier get { return tvOSVendorIdentifier; } } + // please note that we check both advertisingIdentifier/advertisingTrackingEnabled + // usage in scripts to decide if we should enable UNITY_USES_IAD macro (i.e. code that uses iAD and related things) + // that's why it is VERY important that you use private extern functions instead of properties in internal/implementation code + [NativeConditional("PLATFORM_TVOS")] [FreeFunction("UnityAdvertisingIdentifier")] - extern private static string GettvOSAdvertisingIdentifier(); + extern private static string GetTVOSAdvertisingIdentifier(); public static string advertisingIdentifier { get { - string advertisingId = GettvOSAdvertisingIdentifier(); - Application.InvokeOnAdvertisingIdentifierCallback(advertisingId, advertisingTrackingEnabled); + string advertisingId = GetTVOSAdvertisingIdentifier(); + Application.InvokeOnAdvertisingIdentifierCallback(advertisingId, IsTVOSAdvertisingTrackingEnabled()); return advertisingId; } } - extern private static bool tvOSadvertisingTrackingEnabled - { - [NativeConditional("PLATFORM_TVOS")] - [FreeFunction("IOSScripting::IsAdvertisingTrackingEnabled")] get; - } + [NativeConditional("PLATFORM_TVOS")] + [FreeFunction("IOSScripting::IsAdvertisingTrackingEnabled")] + extern private static bool IsTVOSAdvertisingTrackingEnabled(); public static bool advertisingTrackingEnabled { - get { return tvOSadvertisingTrackingEnabled; } + get { return IsTVOSAdvertisingTrackingEnabled(); } }