diff --git a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasInspector.cs b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasInspector.cs index 3ff20a742e..90218d3f3e 100644 --- a/Editor/Mono/2D/SpriteAtlas/SpriteAtlasInspector.cs +++ b/Editor/Mono/2D/SpriteAtlas/SpriteAtlasInspector.cs @@ -61,7 +61,8 @@ class Styles public readonly GUIContent variantMultiplierLabel = EditorGUIUtility.TrTextContent("Scale", "Down scale ratio."); public readonly GUIContent copyMasterButton = EditorGUIUtility.TrTextContent("Copy Master's Settings", "Copy all master's settings into this variant."); public readonly GUIContent packButton = EditorGUIUtility.TrTextContent("Pack Preview", "Pack this atlas."); - public readonly GUIContent disabledPackLabel = EditorGUIUtility.TrTextContent("Sprite Atlas packing is disabled. Enable it in Edit > Project Settings > Editor."); + + public readonly GUIContent disabledPackLabel = EditorGUIUtility.TrTextContent("Sprite Atlas packing is disabled. Enable it in Edit > Settings > Editor.", null, EditorGUIUtility.GetHelpIcon(MessageType.Info)); public readonly GUIContent packableListLabel = EditorGUIUtility.TrTextContent("Objects for Packing", "Only accept Folder, Sprite Sheet(Texture) and Sprite."); public readonly GUIContent notPowerOfTwoWarning = EditorGUIUtility.TrTextContent("This scale will produce a Sprite Atlas variant with a packed texture that is NPOT (non - power of two). This may cause visual artifacts in certain compression/texture formats."); @@ -310,7 +311,10 @@ public override void OnInspectorGUI() } else { - EditorGUILayout.HelpBox(s_Styles.disabledPackLabel.text, MessageType.Info); + if (GUILayout.Button(s_Styles.disabledPackLabel, EditorStyles.helpBox)) + { + SettingsWindow.OpenProjectSettings("Project/Editor"); + } } serializedObject.ApplyModifiedProperties(); diff --git a/Editor/Mono/2D/SpriteEditorModule/SpriteFrameModule/SpriteFrameModuleBaseView.cs b/Editor/Mono/2D/SpriteEditorModule/SpriteFrameModule/SpriteFrameModuleBaseView.cs index d4c6622975..5415fd398d 100644 --- a/Editor/Mono/2D/SpriteEditorModule/SpriteFrameModule/SpriteFrameModuleBaseView.cs +++ b/Editor/Mono/2D/SpriteEditorModule/SpriteFrameModule/SpriteFrameModuleBaseView.cs @@ -200,9 +200,9 @@ private void AddMainUI(VisualElement mainView) if (hasSelected) { var border = selectedSpriteBorder; - border.y = evt.newValue; + border.w = evt.newValue; selectedSpriteBorder = border; - m_BorderFieldT.SetValueWithoutNotify((long)selectedSpriteBorder.y); + m_BorderFieldT.SetValueWithoutNotify((long)selectedSpriteBorder.w); evt.StopPropagation(); } }); @@ -225,9 +225,9 @@ private void AddMainUI(VisualElement mainView) if (hasSelected) { var border = selectedSpriteBorder; - border.w = evt.newValue; + border.y = evt.newValue; selectedSpriteBorder = border; - m_BorderFieldB.SetValueWithoutNotify((long)selectedSpriteBorder.w); + m_BorderFieldB.SetValueWithoutNotify((long)selectedSpriteBorder.y); } }); @@ -335,9 +335,9 @@ protected void PopulateSpriteFrameInspectorField() m_PositionFieldH.SetValueWithoutNotify(Mathf.RoundToInt(spriteRect.height)); var spriteBorder = selectedSpriteBorder; m_BorderFieldL.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.x)); - m_BorderFieldT.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.y)); + m_BorderFieldT.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.w)); m_BorderFieldR.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.z)); - m_BorderFieldB.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.w)); + m_BorderFieldB.SetValueWithoutNotify(Mathf.RoundToInt(spriteBorder.y)); m_PivotField.SetValueWithoutNotify(selectedSpriteAlignment); m_PivotUnitModeField.SetValueWithoutNotify(m_PivotUnitMode); Vector2 pivot = selectedSpritePivotInCurUnitMode; diff --git a/Editor/Mono/Animation/AnimationMode.bindings.cs b/Editor/Mono/Animation/AnimationMode.bindings.cs index c3b411f482..2b30484c29 100644 --- a/Editor/Mono/Animation/AnimationMode.bindings.cs +++ b/Editor/Mono/Animation/AnimationMode.bindings.cs @@ -146,6 +146,9 @@ internal static void StartAnimationRecording() [NativeThrows] extern public static void AddPropertyModification(EditorCurveBinding binding, PropertyModification modification, bool keepPrefabOverride); + [NativeThrows] + extern public static void AddEditorCurveBinding([NotNull] GameObject gameObject, EditorCurveBinding binding); + [NativeThrows] extern internal static void AddTransformTR([NotNull] GameObject root, string path); diff --git a/Editor/Mono/Animation/AnimationUtility.bindings.cs b/Editor/Mono/Animation/AnimationUtility.bindings.cs index dd22c87e44..b2ede18a80 100644 --- a/Editor/Mono/Animation/AnimationUtility.bindings.cs +++ b/Editor/Mono/Animation/AnimationUtility.bindings.cs @@ -143,8 +143,14 @@ internal static EditorCurveBinding[] GetAnimatableBindings(ScriptableObject scri return Internal_GetScriptableObjectAnimatableBindings(scriptableObject); } + internal static EditorCurveBinding[] GetAdditionalAnimatorBindings(GameObject targetObject) + { + return Internal_GetAdditionalAnimatorBindings(targetObject); + } + extern private static EditorCurveBinding[] Internal_GetGameObjectAnimatableBindings([NotNull] GameObject targetObject, [NotNull] GameObject root); extern private static EditorCurveBinding[] Internal_GetScriptableObjectAnimatableBindings([NotNull] ScriptableObject scriptableObject); + extern private static EditorCurveBinding[] Internal_GetAdditionalAnimatorBindings([NotNull] GameObject targetObject); // Binds the property and returns the type of the bound value (Can be used to display special UI for it and to enforce correct drag and drop) // null if it can't be bound. diff --git a/Editor/Mono/Animation/AnimationWindow/AddCurvesPopup.cs b/Editor/Mono/Animation/AnimationWindow/AddCurvesPopup.cs index 84e4b40e80..7bece927e0 100644 --- a/Editor/Mono/Animation/AnimationWindow/AddCurvesPopup.cs +++ b/Editor/Mono/Animation/AnimationWindow/AddCurvesPopup.cs @@ -13,6 +13,11 @@ namespace UnityEditorInternal internal class AddCurvesPopup : EditorWindow { const float k_WindowPadding = 3; + const float k_SpaceForSlider = 16; + + const float k_WindowMaxWidth = 450; + const float k_WindowMinWidth = 240; + const float k_WindowFixedHeight = 250; internal static AnimationWindowState s_State; @@ -20,16 +25,25 @@ internal class AddCurvesPopup : EditorWindow private static long s_LastClosedTime; private static AddCurvesPopupHierarchy s_Hierarchy; - private static Vector2 windowSize = new Vector2(240, 250); - public delegate void OnNewCurveAdded(AddCurvesPopupPropertyNode node); private static OnNewCurveAdded NewCurveAddedCallback; + Vector2 GetWindowSize() + { + float contentWidth = s_Hierarchy.GetContentWidth(); + float width = Mathf.Clamp(contentWidth + k_SpaceForSlider + k_WindowPadding, k_WindowMinWidth, k_WindowMaxWidth); + return new Vector2(width, k_WindowFixedHeight); + } + void Init(Rect buttonRect) { + s_Hierarchy = new AddCurvesPopupHierarchy(); + s_Hierarchy.InitIfNeeded(this, new Rect(0, 0, k_WindowMinWidth, k_WindowFixedHeight)); + buttonRect = GUIUtility.GUIToScreenRect(buttonRect); - ShowAsDropDown(buttonRect, windowSize, new[] { PopupLocation.Right }); + + ShowAsDropDown(buttonRect, GetWindowSize(), new[] { PopupLocation.Right }); } void OnEnable() @@ -77,8 +91,7 @@ internal void OnGUI() if (Event.current.type == EventType.Layout) return; - if (s_Hierarchy == null) - s_Hierarchy = new AddCurvesPopupHierarchy(); + Vector2 windowSize = GetWindowSize(); Rect rect = new Rect(1, 1, windowSize.x - k_WindowPadding, windowSize.y - k_WindowPadding); GUI.Box(new Rect(0, 0, windowSize.x, windowSize.y), GUIContent.none, "grey_border"); diff --git a/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchy.cs b/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchy.cs index 3f69454e6f..4adb9ff208 100644 --- a/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchy.cs +++ b/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchy.cs @@ -14,9 +14,16 @@ internal class AddCurvesPopupHierarchy private TreeViewState m_TreeViewState; private AddCurvesPopupHierarchyDataSource m_TreeViewDataSource; + private float m_ContentWidth = 0f; + + public float GetContentWidth() + { + return m_ContentWidth; + } + public void OnGUI(Rect position, EditorWindow owner) { - InitIfNeeded(owner, position); + m_TreeView.SetTotalRect(position); m_TreeView.OnEvent(); m_TreeView.OnGUI(position, GUIUtility.GetControlID(FocusType.Keyboard)); } @@ -33,7 +40,7 @@ public void InitIfNeeded(EditorWindow owner, Rect rect) m_TreeView.deselectOnUnhandledMouseDown = true; m_TreeViewDataSource = new AddCurvesPopupHierarchyDataSource(m_TreeView); - TreeViewGUI gui = new AddCurvesPopupHierarchyGUI(m_TreeView, owner); + AddCurvesPopupHierarchyGUI gui = new AddCurvesPopupHierarchyGUI(m_TreeView, owner); m_TreeView.Init(rect, m_TreeViewDataSource, @@ -42,6 +49,8 @@ public void InitIfNeeded(EditorWindow owner, Rect rect) ); m_TreeViewDataSource.UpdateData(); + + m_ContentWidth = gui.GetContentWidth(); } internal virtual bool IsRenamingNodeAllowed(TreeViewItem node) diff --git a/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchyDataSource.cs b/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchyDataSource.cs index b944bfb74b..5bea4058b3 100644 --- a/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchyDataSource.cs +++ b/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchyDataSource.cs @@ -31,6 +31,7 @@ private void SetupRootNodeSettings() public override void FetchData() { + m_RootItem = null; if (AddCurvesPopup.s_State.selection.canAddCurves) { GameObject rootGameObject = AddCurvesPopup.s_State.activeRootGameObject; diff --git a/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchyGUI.cs b/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchyGUI.cs index 37a14c36a6..939b21cf85 100644 --- a/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchyGUI.cs +++ b/Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchyGUI.cs @@ -5,6 +5,7 @@ using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEngine; +using System.Collections.Generic; namespace UnityEditorInternal { @@ -14,6 +15,7 @@ internal class AddCurvesPopupHierarchyGUI : TreeViewGUI public bool showPlusButton { get; set; } private GUIStyle plusButtonStyle = "OL Plus"; private GUIStyle plusButtonBackgroundStyle = "Tag MenuItem"; + private GUIContent addPropertiesContent = EditorGUIUtility.TrTextContent("Add Properties"); private const float plusButtonWidth = 17; public AddCurvesPopupHierarchyGUI(TreeViewController treeView, EditorWindow owner) @@ -25,7 +27,12 @@ public AddCurvesPopupHierarchyGUI(TreeViewController treeView, EditorWindow owne public override void OnRowGUI(Rect rowRect, TreeViewItem node, int row, bool selected, bool focused) { base.OnRowGUI(rowRect, node, row, selected, focused); + DoAddCurveButton(rowRect, node); + HandleContextMenu(rowRect, node); + } + private void DoAddCurveButton(Rect rowRect, TreeViewItem node) + { // Is it propertynode. If not, then we don't need plusButton so quit here AddCurvesPopupPropertyNode hierarchyNode = node as AddCurvesPopupPropertyNode; if (hierarchyNode == null || hierarchyNode.curveBindings == null || hierarchyNode.curveBindings.Length == 0) @@ -41,10 +48,85 @@ public override void OnRowGUI(Rect rowRect, TreeViewItem node, int row, bool sel if (GUI.Button(buttonRect, GUIContent.none, plusButtonStyle)) { AddCurvesPopup.AddNewCurve(hierarchyNode); - owner.Close(); + + // Hold shift key to add new curves and keep window opened. + if (Event.current.shift) + m_TreeView.ReloadData(); + else + owner.Close(); } } + private void HandleContextMenu(Rect rowRect, TreeViewItem node) + { + if (Event.current.type != EventType.ContextClick) + return; + + if (rowRect.Contains(Event.current.mousePosition)) + { + // Add current node to selection + var ids = new List(m_TreeView.GetSelection()); + ids.Add(node.id); + m_TreeView.SetSelection(ids.ToArray(), false, false); + + GenerateMenu().ShowAsContext(); + Event.current.Use(); + } + } + + private GenericMenu GenerateMenu() + { + GenericMenu menu = new GenericMenu(); + menu.AddItem(addPropertiesContent, false, AddPropertiesFromSelectedNodes); + + return menu; + } + + private void AddPropertiesFromSelectedNodes() + { + int[] ids = m_TreeView.GetSelection(); + for (int i = 0; i < ids.Length; ++i) + { + var node = m_TreeView.FindItem(ids[i]); + var propertyNode = node as AddCurvesPopupPropertyNode; + + if (propertyNode != null) + { + AddCurvesPopup.AddNewCurve(propertyNode); + } + else if (node.hasChildren) + { + foreach (var childNode in node.children) + { + var childPropertyNode = childNode as AddCurvesPopupPropertyNode; + if (childPropertyNode != null) + { + AddCurvesPopup.AddNewCurve(childPropertyNode); + } + } + } + } + + m_TreeView.ReloadData(); + } + + public float GetContentWidth() + { + IList rows = m_TreeView.data.GetRows(); + List allRows = new List(); + allRows.AddRange(rows); + + for (int i = 0; i < allRows.Count; ++i) + { + var row = allRows[i]; + if (row.hasChildren) + allRows.AddRange(row.children); + } + + float rowWidth = GetMaxWidth(allRows); + return rowWidth + plusButtonWidth; + } + override protected void SyncFakeItem() { //base.SyncFakeItem(); diff --git a/Editor/Mono/Animation/AnimationWindow/CurveEditor.cs b/Editor/Mono/Animation/AnimationWindow/CurveEditor.cs index be328b707c..148c5711eb 100644 --- a/Editor/Mono/Animation/AnimationWindow/CurveEditor.cs +++ b/Editor/Mono/Animation/AnimationWindow/CurveEditor.cs @@ -51,6 +51,13 @@ public CurveWrapper() setAxisUiScalarsCallback = null; } + // Fix for case 1086532 - Audio source inspector leaks memory + ~CurveWrapper() + { + if (m_Renderer != null) + m_Renderer.FlushCache(); + } + internal enum SelectionMode { None = 0, diff --git a/Editor/Mono/Animation/GameObjectRecorder.bindings.cs b/Editor/Mono/Animation/GameObjectRecorder.bindings.cs index c5ee0ddd49..de7c75bede 100644 --- a/Editor/Mono/Animation/GameObjectRecorder.bindings.cs +++ b/Editor/Mono/Animation/GameObjectRecorder.bindings.cs @@ -46,7 +46,17 @@ public void BindComponentsOfType(GameObject target, Type componentType, bool rec extern public GameObject root { get; } // Bindings. - extern public void Bind(EditorCurveBinding binding); + public void Bind(EditorCurveBinding binding) + { + if (!binding.type.IsSubclassOf(typeof(UnityEngine.Object))) + throw new InvalidCastException("Binding type should derive from Unity type."); + + BindInternal(binding); + } + + [NativeMethod("Bind")] + extern private void BindInternal(EditorCurveBinding binding); + extern public void BindAll(GameObject target, bool recursive); extern public void BindComponent([NotNull] Component component); diff --git a/Editor/Mono/Animation/ZoomableArea.cs b/Editor/Mono/Animation/ZoomableArea.cs index 80017a2a7e..8ab4fd6020 100644 --- a/Editor/Mono/Animation/ZoomableArea.cs +++ b/Editor/Mono/Animation/ZoomableArea.cs @@ -377,14 +377,20 @@ public void SetShownHRange(float min, float max) public void SetShownVRangeInsideMargins(float min, float max) { + float heightInsideMargins = drawRect.height - topmargin - bottommargin; + if (heightInsideMargins < kMinHeight) heightInsideMargins = kMinHeight; + + float denum = max - min; + if (denum < kMinHeight) denum = kMinHeight; + if (m_UpDirection == YDirection.Positive) { - m_Scale.y = -(drawRect.height - topmargin - bottommargin) / (max - min); + m_Scale.y = -heightInsideMargins / denum; m_Translation.y = drawRect.height - min * m_Scale.y - topmargin; } else { - m_Scale.y = (drawRect.height - topmargin - bottommargin) / (max - min); + m_Scale.y = heightInsideMargins / denum; m_Translation.y = -min * m_Scale.y - bottommargin; } EnforceScaleAndRange(); @@ -392,14 +398,17 @@ public void SetShownVRangeInsideMargins(float min, float max) public void SetShownVRange(float min, float max) { + float denum = max - min; + if (denum < kMinHeight) denum = kMinHeight; + if (m_UpDirection == YDirection.Positive) { - m_Scale.y = -drawRect.height / (max - min); + m_Scale.y = -drawRect.height / denum; m_Translation.y = drawRect.height - min * m_Scale.y; } else { - m_Scale.y = drawRect.height / (max - min); + m_Scale.y = drawRect.height / denum; m_Translation.y = -min * m_Scale.y; } EnforceScaleAndRange(); @@ -983,5 +992,20 @@ public float PixelDeltaToTime(Rect rect) { return shownArea.width / rect.width; } + + public void UpdateZoomScale(float fMaxScaleValue, float fMinScaleValue) + { + // Update/reset the values of the scale to new zoom range, if the current values do not fall in the range of the new resolution + + if (m_Scale.y > fMaxScaleValue || m_Scale.y < fMinScaleValue) + { + m_Scale.y = m_Scale.y > fMaxScaleValue ? fMaxScaleValue : fMinScaleValue; + } + + if (m_Scale.x > fMaxScaleValue || m_Scale.x < fMinScaleValue) + { + m_Scale.x = m_Scale.x > fMaxScaleValue ? fMaxScaleValue : fMinScaleValue; + } + } } } // namespace diff --git a/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs b/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs index 001ec95022..90bdb7af92 100644 --- a/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs +++ b/Editor/Mono/AssetPipeline/TextureImporter.bindings.cs @@ -20,6 +20,14 @@ namespace UnityEditor [NativeHeader("Editor/Src/EditorUserBuildSettings.h")] public sealed partial class TextureImporter : AssetImporter, ISpriteEditorDataProvider { + private string GetFixedPlatformName(string platform) + { + var targetGroup = BuildPipeline.GetBuildTargetGroupByName(platform); + if (targetGroup != BuildTargetGroup.Unknown) + return BuildPipeline.GetBuildTargetGroupName(targetGroup); + return platform; + } + [Obsolete("textureFormat is no longer accessible at the TextureImporter level. For old 'simple' formats use the textureCompression property for the equivalent automatic choice (Uncompressed for TrueColor, Compressed and HQCommpressed for 16 bits). For platform specific formats use the [[PlatformTextureSettings]] API. Using this setter will setup various parameters to match the new automatic system as well as possible. Getter will return the last value set.")] public extern TextureImporterFormat textureFormat { @@ -96,6 +104,9 @@ public bool GetPlatformTextureSettings(string platform, out int maxTextureSize, // public API will always return a valid TextureImporterPlatformSettings, creating it based on the default one if it did not exist. public TextureImporterPlatformSettings GetPlatformTextureSettings(string platform) { + // make sure we are converting the settings to use the proper BuildTargetGroupName to get them (the way it works on other importers) + platform = GetFixedPlatformName(platform); + TextureImporterPlatformSettings dest = GetPlatformTextureSetting_Internal(platform); if (platform != dest.name) { @@ -112,6 +123,7 @@ public TextureImporterPlatformSettings GetDefaultPlatformTextureSettings() public TextureImporterFormat GetAutomaticFormat(string platform) { + platform = GetFixedPlatformName(platform); TextureImporterSettings settings = new TextureImporterSettings(); ReadTextureSettings(settings); TextureImporterPlatformSettings platformSettings = GetPlatformTextureSettings(platform); @@ -161,11 +173,25 @@ public void SetPlatformTextureSettings(string platform, int maxTextureSize, Text SetPlatformTextureSettings(dest); } + [NativeName("SetPlatformTextureSettings")] + private extern void SetPlatformTextureSettings_Internal(TextureImporterPlatformSettings platformSettings); + // Set specific target platform settings - public extern void SetPlatformTextureSettings(TextureImporterPlatformSettings platformSettings); + public void SetPlatformTextureSettings(TextureImporterPlatformSettings platformSettings) + { + // we need to fix the name in case the user changed it to some mismatching value + platformSettings.name = GetFixedPlatformName(platformSettings.name); + SetPlatformTextureSettings_Internal(platformSettings); + } // Clear specific target platform settings - public extern void ClearPlatformTextureSettings(string platform); + [NativeName("ClearPlatformTextureSettings")] + private extern void ClearPlatformTextureSettings_Internal(string platform); + + public void ClearPlatformTextureSettings(string platform) + { + ClearPlatformTextureSettings_Internal(GetFixedPlatformName(platform)); + } [FreeFunction] internal static extern TextureImporterFormat DefaultFormatFromTextureParameters([NotNull] TextureImporterSettings settings, TextureImporterPlatformSettings platformSettings, bool doesTextureContainAlpha, bool sourceWasHDR, BuildTarget destinationPlatform); diff --git a/Editor/Mono/AssetPostprocessor.cs b/Editor/Mono/AssetPostprocessor.cs index 798978522f..00ba6ae26f 100644 --- a/Editor/Mono/AssetPostprocessor.cs +++ b/Editor/Mono/AssetPostprocessor.cs @@ -263,10 +263,10 @@ static string GetMeshProcessorsHashString() var inst = Activator.CreateInstance(assetPostprocessorClass) as AssetPostprocessor; var type = inst.GetType(); bool hasPreProcessMethod = type.GetMethod("OnPreprocessModel", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) != null; - bool hasProcessMeshAssignMethod = type.GetMethod("OnProcessMeshAssingModel", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) != null; + bool hasPostprocessMeshHierarchy = type.GetMethod("OnPostprocessMeshHierarchy", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) != null; bool hasPostProcessMethod = type.GetMethod("OnPostprocessModel", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) != null; uint version = inst.GetVersion(); - if (version != 0 && (hasPreProcessMethod || hasProcessMeshAssignMethod || hasPostProcessMethod)) + if (version != 0 && (hasPreProcessMethod || hasPostprocessMeshHierarchy || hasPostProcessMethod)) { versionsByType.Add(type.FullName, version); } @@ -356,6 +356,16 @@ static bool ProcessMeshHasAssignMaterial() return false; } + [RequiredByNativeCode] + static void PostprocessMeshHierarchy(GameObject root) + { + foreach (AssetPostprocessor inst in m_ImportProcessors) + { + object[] args = { root }; + AttributeHelper.InvokeMemberIfAvailable(inst, "OnPostprocessMeshHierarchy", args); + } + } + static void PostprocessMesh(GameObject gameObject) { foreach (AssetPostprocessor inst in m_ImportProcessors) diff --git a/Editor/Mono/AssetStore/AssetStoreContext.cs b/Editor/Mono/AssetStore/AssetStoreContext.cs index 068e39c522..7669651041 100644 --- a/Editor/Mono/AssetStore/AssetStoreContext.cs +++ b/Editor/Mono/AssetStore/AssetStoreContext.cs @@ -12,6 +12,7 @@ using System.Linq; using System.IO; using UnityEditor.Web; +using UnityEditor.Analytics; namespace UnityEditor { @@ -163,6 +164,15 @@ public void OpenBrowser(string url) Application.OpenURL(url); } + [Serializable] + public struct DownloadAssetInfo + { + public string package_id; + public string package_name; + public string publisher_name; + public string category_name; + } + public void Download(Package package, DownloadInfo downloadInfo) { Download( @@ -205,6 +215,13 @@ public static void Download(string package_id, string url, string key, string pa parameters["download"] = download; AssetStoreUtils.Download(package_id, url, dest, key, parameters.ToString(), resumeOK, doneCallback); + EditorAnalytics.SendAssetDownloadEvent(new DownloadAssetInfo() + { + package_id = package_id, + package_name = package_name, + publisher_name = publisher_name, + category_name = category_name + }); } /// diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParameterView.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParameterView.cs index 9832a5c637..8c86a45d7a 100644 --- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParameterView.cs +++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParameterView.cs @@ -106,6 +106,10 @@ void DrawElement(Rect rect, int index, bool isActive, bool isFocused) public Vector2 CalcSize() { + if (m_ReorderableListWithRenameAndScrollView.list.count != m_Controller.exposedParameters.Length) + { + RecreateListControl(); + } float maxWidth = 0; for (int index = 0; index < m_ReorderableListWithRenameAndScrollView.list.count; index++) { diff --git a/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParametersPopup.cs b/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParametersPopup.cs index c2935d9343..b06409045d 100644 --- a/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParametersPopup.cs +++ b/Editor/Mono/Audio/Mixer/GUI/AudioMixerExposedParametersPopup.cs @@ -18,7 +18,7 @@ internal static void Popup(AudioMixerController controller, GUIStyle style, para Rect buttonRect = GUILayoutUtility.GetRect(content, style, options); if (EditorGUI.DropdownButton(buttonRect, content, FocusType.Passive, style)) { - PopupWindow.Show(buttonRect, new AudioMixerExposedParametersPopup(controller), null, ShowMode.PopupMenuWithKeyboardFocus); + PopupWindow.Show(buttonRect, new AudioMixerExposedParametersPopup(controller)); } } diff --git a/Editor/Mono/BuildPipeline.bindings.cs b/Editor/Mono/BuildPipeline.bindings.cs index 87dd7ed3cc..64e3257ace 100644 --- a/Editor/Mono/BuildPipeline.bindings.cs +++ b/Editor/Mono/BuildPipeline.bindings.cs @@ -9,6 +9,7 @@ using UnityEngine.Bindings; using UnityEditor.Build.Reporting; using Mono.Cecil; +using UnityEditor.Scripting.ScriptCompilation; namespace UnityEditor { @@ -179,6 +180,7 @@ public class BuildPipeline internal static extern BuildTargetGroup GetBuildTargetGroupByName(string platform); internal static extern BuildTarget GetBuildTargetByName(string platform); + internal static extern EditorScriptCompilationOptions GetScriptCompileFlags(BuildOptions buildOptions, BuildTarget buildTarget); [FreeFunction] internal static extern string GetBuildTargetGroupDisplayName(BuildTargetGroup targetPlatformGroup); diff --git a/Editor/Mono/BuildPipeline/AssemblyStripper.cs b/Editor/Mono/BuildPipeline/AssemblyStripper.cs index 69c450511f..94da574fe4 100644 --- a/Editor/Mono/BuildPipeline/AssemblyStripper.cs +++ b/Editor/Mono/BuildPipeline/AssemblyStripper.cs @@ -77,7 +77,7 @@ private static bool StripAssembliesTo(string[] assemblies, string[] searchDirs, args.AddRange(additionalBlacklist.Select(path => "-x \"" + path + "\"")); args.AddRange(searchDirs.Select(d => "-d \"" + d + "\"")); - args.AddRange(assemblies.Select(assembly => "-a \"" + Path.GetFullPath(assembly) + "\"")); + args.AddRange(assemblies.Select(assembly => "--include-unity-root-assembly=\"" + Path.GetFullPath(assembly) + "\"")); args.Add($"--dotnetruntime={GetRuntimeArgumentValueForLinker(buildTargetGroup)}"); args.Add($"--dotnetprofile={GetProfileArgumentValueForLinker(buildTargetGroup)}"); args.Add("--use-editor-options"); @@ -270,7 +270,6 @@ private static void RunAssemblyStripper(IEnumerable assemblies, string managedAs blacklists = blacklists.Concat(new[] { WriteMethodsToPreserveBlackList(rcr, platformProvider.target), - WriteUnityEngineBlackList(), MonoAssemblyStripping.GenerateLinkXmlToPreserveDerivedTypes(managedAssemblyFolderPath, rcr) }); } @@ -377,15 +376,6 @@ private static string WriteMethodsToPreserveBlackList(RuntimeClassRegistry rcr, return methodPerserveBlackList; } - private static string WriteUnityEngineBlackList() - { - // UnityEngine.dll would be stripped, as it contains no referenced symbols, only type forwarders. - // Since we need those type forwarders, we generate blacklist to preserve the assembly (but no members). - var unityEngineBlackList = Path.GetTempFileName(); - File.WriteAllText(unityEngineBlackList, ""); - return unityEngineBlackList; - } - private static string GetMethodPreserveBlacklistContents(RuntimeClassRegistry rcr, BuildTarget target) { if (rcr.GetMethodsToPreserve().Count == 0) diff --git a/Editor/Mono/Camera/BuiltinBakedReflectionSystem.bindings.cs b/Editor/Mono/Camera/BuiltinBakedReflectionSystem.bindings.cs index 7928108d9b..c2e20ef94f 100644 --- a/Editor/Mono/Camera/BuiltinBakedReflectionSystem.bindings.cs +++ b/Editor/Mono/Camera/BuiltinBakedReflectionSystem.bindings.cs @@ -64,6 +64,14 @@ public void Clear() Internal_Clear(); } + public bool BakeAllReflectionProbes() + { + if (disposed) + throw new ObjectDisposedException("BuiltinBakedReflectionSystem"); + + return Internal_BakeAllReflectionProbes(); + } + public void Cancel() { // Cancel is empty on purpose @@ -120,5 +128,6 @@ void Internal_BuiltinBakedReflectionSystem_SetIsDone(bool isDone) extern void Internal_SynchronizeReflectionProbes(); extern void Internal_Clear(); extern void Internal_SetPtr(BuiltinBakedReflectionSystem ptr); + extern bool Internal_BakeAllReflectionProbes(); } } diff --git a/Editor/Mono/Camera/IScriptableBakedReflectionSystem.cs b/Editor/Mono/Camera/IScriptableBakedReflectionSystem.cs index bcb1c439d4..2557f60973 100644 --- a/Editor/Mono/Camera/IScriptableBakedReflectionSystem.cs +++ b/Editor/Mono/Camera/IScriptableBakedReflectionSystem.cs @@ -16,5 +16,6 @@ public interface IScriptableBakedReflectionSystem : IDisposable void SynchronizeReflectionProbes(); void Clear(); void Cancel(); + bool BakeAllReflectionProbes(); } } diff --git a/Editor/Mono/Camera/ScriptableBakedReflectionSystem.cs b/Editor/Mono/Camera/ScriptableBakedReflectionSystem.cs index 45f1f55623..416f7574a1 100644 --- a/Editor/Mono/Camera/ScriptableBakedReflectionSystem.cs +++ b/Editor/Mono/Camera/ScriptableBakedReflectionSystem.cs @@ -21,6 +21,7 @@ public virtual void Tick(SceneStateHash sceneStateHash, IScriptableBakedReflecti public virtual void SynchronizeReflectionProbes() {} public virtual void Clear() {} public virtual void Cancel() {} + public virtual bool BakeAllReflectionProbes() { return false; } protected virtual void Dispose(bool disposing) {} diff --git a/Editor/Mono/Camera/ScriptableBakedReflectionSystemWrapper.bindings.cs b/Editor/Mono/Camera/ScriptableBakedReflectionSystemWrapper.bindings.cs index e2c26a0746..96d7c7cdcc 100644 --- a/Editor/Mono/Camera/ScriptableBakedReflectionSystemWrapper.bindings.cs +++ b/Editor/Mono/Camera/ScriptableBakedReflectionSystemWrapper.bindings.cs @@ -118,6 +118,18 @@ void Internal_ScriptableBakedReflectionSystemWrapper_Cancel() implementation.Cancel(); } + [RequiredByNativeCode] + bool Internal_ScriptableBakedReflectionSystemWrapper_BakeAllReflectionProbes() + { + if (Disposed) + throw new ObjectDisposedException("ScriptableBakedReflectionSystemWrapper"); + + if (implementation != null) + return implementation.BakeAllReflectionProbes(); + + return false; + } + [StaticAccessor("ScriptableBakedReflectionSystem", StaticAccessorType.DoubleColon)] static extern void ScriptingEnterStage(IntPtr objPtr, int stage, string progressMessage, float progress); diff --git a/Editor/Mono/Collab/Collab.bindings.cs b/Editor/Mono/Collab/Collab.bindings.cs index e2755d5759..253f72c7b9 100644 --- a/Editor/Mono/Collab/Collab.bindings.cs +++ b/Editor/Mono/Collab/Collab.bindings.cs @@ -206,12 +206,17 @@ public extern void TestClearSoftLockAsCollaborator(string projectGuid, string pr [NativeMethod(HasExplicitThis = true, ThrowsException = true, IsThreadSafe = true)] extern void SetChangesToPublishInternal(ChangeItem[] changes); + [NativeMethod(HasExplicitThis = true, ThrowsException = true, IsThreadSafe = true)] + extern Change[] GetSelectedChangesInternal(); + [NativeMethod(Name = "GetJobProgress", HasExplicitThis = true, ThrowsException = true)] extern bool GetJobProgressInternal([Out] ProgressInfo info, int jobId); [NativeMethod(HasExplicitThis = true, ThrowsException = true)] public extern void Publish(string comment, bool useSelectedAssets, bool confirmMatchesPrevious); + [NativeMethod(HasExplicitThis = true, ThrowsException = true, IsThreadSafe = true)] + public extern void ClearSelectedChangesToPublish(); [NativeMethod(HasExplicitThis = true, ThrowsException = true)] public extern SoftLock[] GetSoftLocks(string assetGuid); diff --git a/Editor/Mono/Collab/Collab.cs b/Editor/Mono/Collab/Collab.cs index 2ab706e9eb..5a3a96c851 100644 --- a/Editor/Mono/Collab/Collab.cs +++ b/Editor/Mono/Collab/Collab.cs @@ -365,10 +365,21 @@ public bool SetConflictsResolvedTheirs(string[] paths) public PublishInfo GetChangesToPublish() { Change[] changes = GetChangesToPublishInternal(); + bool isFiltered = false; + + if (SupportsAsyncChanges()) + { + changes = GetSelectedChangesInternal(); + if (Toolbar.isLastShowRequestPartial) + { + isFiltered = true; + } + } + return new PublishInfo() { changes = changes, - filter = false + filter = isFiltered }; } diff --git a/Editor/Mono/Commands/GOCreationCommands.cs b/Editor/Mono/Commands/GOCreationCommands.cs index bc37a4b193..17755458d3 100644 --- a/Editor/Mono/Commands/GOCreationCommands.cs +++ b/Editor/Mono/Commands/GOCreationCommands.cs @@ -52,7 +52,12 @@ static void CreateEmptyChild(MenuCommand menuCommand) { var parent = menuCommand.context as GameObject; if (parent == null) - parent = Selection.activeGameObject; + { + var activeGO = Selection.activeGameObject; + if (activeGO != null && !EditorUtility.IsPersistent(activeGO)) + parent = activeGO; + } + var go = ObjectFactory.CreateGameObject("GameObject"); Place(go, parent); } diff --git a/Editor/Mono/ConsoleWindow.cs b/Editor/Mono/ConsoleWindow.cs index 12d7ac5db0..4cb9501d5d 100644 --- a/Editor/Mono/ConsoleWindow.cs +++ b/Editor/Mono/ConsoleWindow.cs @@ -627,6 +627,7 @@ void OnGUI() EditorGUIUtility.SetIconSize(new Vector2(rowHeight, rowHeight)); GUIContent tempContent = new GUIContent(); int id = GUIUtility.GetControlID(0); + int rowDoubleClicked = -1; /////@TODO: Make Frame selected work with ListViewState using (new GettingLogEntriesScope(m_ListView)) @@ -712,11 +713,16 @@ void OnGUI() if (openSelectedItem) { - LogEntries.RowGotDoubleClicked(selectedRow); - Event.current.Use(); + rowDoubleClicked = selectedRow; + e.Use(); } } + // Prevent dead locking in EditorMonoConsole by delaying callbacks (which can log to the console) until after LogEntries.EndGettingEntries() has been + // called (this releases the mutex in EditorMonoConsole so logging again is allowed). Fix for case 1081060. + if (rowDoubleClicked != -1) + LogEntries.RowGotDoubleClicked(rowDoubleClicked); + EditorGUIUtility.SetIconSize(Vector2.zero); // Display active text (We want word wrapped text with a vertical scrollbar) diff --git a/Editor/Mono/ContainerWindow.bindings.cs b/Editor/Mono/ContainerWindow.bindings.cs index 2c427c9ead..d070126ab3 100644 --- a/Editor/Mono/ContainerWindow.bindings.cs +++ b/Editor/Mono/ContainerWindow.bindings.cs @@ -12,7 +12,7 @@ internal enum ShowMode { // Show as a normal window with max, min & close buttons. NormalWindow = 0, - // Used for a popup menu and tooltip. On mac this means light shadow and no titlebar. + // Used for a popup menu. On mac this means light shadow and no titlebar. PopupMenu = 1, // Utility window - floats above the app. Disappears when app loses focus. Utility = 2, @@ -22,8 +22,8 @@ internal enum ShowMode MainWindow = 4, // Aux windows. The ones that close the moment you move the mouse out of them. AuxWindow = 5, - // Like PopupMenu, but allows keyboard focus (e.g. AddComponentWindow) - PopupMenuWithKeyboardFocus = 6 + // Like PopupMenu, but without keyboard focus + Tooltip = 6 } //[StaticAccessor("ContainerWindowBindings", StaticAccessorType.DoubleColon)] diff --git a/Editor/Mono/ContainerWindow.cs b/Editor/Mono/ContainerWindow.cs index dea9694434..c1a379d446 100644 --- a/Editor/Mono/ContainerWindow.cs +++ b/Editor/Mono/ContainerWindow.cs @@ -59,20 +59,31 @@ internal void __internalAwake() internal static bool IsPopup(ShowMode mode) { - return (ShowMode.PopupMenu == mode || ShowMode.PopupMenuWithKeyboardFocus == mode); + return (ShowMode.PopupMenu == mode); } internal bool isPopup { get { return IsPopup((ShowMode)m_ShowMode); } } internal void ShowPopup() { - m_ShowMode = (int)ShowMode.PopupMenu; + ShowPopupWithMode(ShowMode.PopupMenu); + } + + internal void ShowTooltip() + { + ShowPopupWithMode(ShowMode.Tooltip); + } + + internal void ShowPopupWithMode(ShowMode mode) + { + m_ShowMode = (int)mode; Internal_Show(m_PixelRect, m_ShowMode, m_MinSize, m_MaxSize); if (m_RootView) m_RootView.SetWindowRecurse(this); Internal_SetTitle(m_Title); Save(); - Internal_BringLiveAfterCreation(false, false); + // only set focus iff mode is a popupMenu. + Internal_BringLiveAfterCreation(false, mode == ShowMode.PopupMenu); } static Color skinBackgroundColor @@ -84,7 +95,7 @@ static Color skinBackgroundColor } // Show the editor window. - public void Show(ShowMode showMode, bool loadPosition, bool displayImmediately) + public void Show(ShowMode showMode, bool loadPosition, bool displayImmediately, bool setFocus) { if (showMode == ShowMode.AuxWindow) showMode = ShowMode.Utility; @@ -107,7 +118,7 @@ public void Show(ShowMode showMode, bool loadPosition, bool displayImmediately) SetBackgroundColor(skinBackgroundColor); - Internal_BringLiveAfterCreation(displayImmediately, true); + Internal_BringLiveAfterCreation(displayImmediately, setFocus); // Window could be killed by now in user callbacks... if (!this) diff --git a/Editor/Mono/EditorApplication.cs b/Editor/Mono/EditorApplication.cs index ef237e355f..9d941b3959 100644 --- a/Editor/Mono/EditorApplication.cs +++ b/Editor/Mono/EditorApplication.cs @@ -187,6 +187,11 @@ public static void DirtyHierarchyWindowSorting() // Global key up/down event that was not handled by anyone internal static CallbackFunction globalEventHandler; + // Returns true when the pressed keys are defined in the Trigger + internal static Func doPressedKeysTriggerAnyShortcut; + + internal static event Action focusChanged; + // Windows were reordered internal static CallbackFunction windowsReordered; @@ -313,6 +318,14 @@ static void Internal_CallWindowsReordered() windowsReordered(); } + [RequiredByNativeCode] + static bool DoPressedKeysTriggerAnyShortcutHandler() + { + if (doPressedKeysTriggerAnyShortcut != null) + return doPressedKeysTriggerAnyShortcut(); + return false; + } + [RequiredByNativeCode] static void Internal_CallGlobalEventHandler() { @@ -324,5 +337,11 @@ static void Internal_CallGlobalEventHandler() Event.current = null; } + + [RequiredByNativeCode] + static void Internal_FocusChanged(bool isFocused) + { + focusChanged?.Invoke(isFocused); + } } } diff --git a/Editor/Mono/EditorGUI.cs b/Editor/Mono/EditorGUI.cs index 6b63726c98..8fad8a4c1f 100644 --- a/Editor/Mono/EditorGUI.cs +++ b/Editor/Mono/EditorGUI.cs @@ -18,6 +18,7 @@ using UnityEditor.Build; using UnityEditor.StyleSheets; using UnityEngine.Internal; +using UnityEngine.Rendering; using DescriptionAttribute = System.ComponentModel.DescriptionAttribute; namespace UnityEditor @@ -1050,6 +1051,7 @@ internal static string DoTextField(RecycledTextEditor editor, int id, Rect posit // Note, OS X send characters for the following keys that we need to eat: // ASCII 25: "End Of Medium" on pressing shift tab // ASCII 27: "Escape" on pressing ESC + nonPrintableTab = true; } else if (editor.IsEditingControl(id)) { @@ -2534,7 +2536,7 @@ private static float DoSlider( if (sliderBackground != null && Event.current.type == EventType.Repaint) { var bgRect = sliderStyle.overflow.Add(sliderStyle.padding.Remove(sliderRect)); - Graphics.DrawTexture(bgRect, sliderBackground, new Rect(.5f / sliderBackground.width, .5f / sliderBackground.height, 1 - 1f / sliderBackground.width, 1 - 1f / sliderBackground.height), 0, 0, 0, 0, Color.grey); + Graphics.DrawTexture(bgRect, sliderBackground, new Rect(.5f / sliderBackground.width, .5f / sliderBackground.height, 1 - 1f / sliderBackground.width, 1 - 1f / sliderBackground.height), 0, 0, 0, 0, new Color(0.5f, 0.5f, 0.5f, 0.5f)); } newSliderValue = GUI.Slider(sliderRect, newSliderValue, 0, remapLeft, remapRight, sliderStyle, showMixedValue ? "SliderMixed" : thumbStyle, true, sliderId); @@ -4337,6 +4339,14 @@ private static Color DoColorField(Rect position, int id, Color value, bool showE break; case EventType.ExecuteCommand: + + // Cancel EyeDropper if we change focus. + if (showEyedropper && Event.current.commandName == EventCommandNames.NewKeyboardFocus) + { + EyeDropper.End(); + s_ColorPickID = 0; + } + // when ColorPicker sends an event back to this control's GUIView, it someties retains keyboardControl if (GUIUtility.keyboardControl == id || ColorPicker.originalKeyboardControl == id) { @@ -5639,11 +5649,11 @@ internal static void ShowRepaints() // Draws the alpha channel of a texture within a rectangle. internal static void DrawTextureAlphaInternal(Rect position, Texture image, ScaleMode scaleMode, float imageAspect, float mipLevel) { - DrawPreviewTextureInternal(position, image, alphaMaterial, scaleMode, imageAspect, mipLevel); + DrawPreviewTextureInternal(position, image, alphaMaterial, scaleMode, imageAspect, mipLevel, ColorWriteMask.All); } // Draws texture transparently using the alpha channel. - internal static void DrawTextureTransparentInternal(Rect position, Texture image, ScaleMode scaleMode, float imageAspect, float mipLevel) + internal static void DrawTextureTransparentInternal(Rect position, Texture image, ScaleMode scaleMode, float imageAspect, float mipLevel, ColorWriteMask colorWriteMask) { if (imageAspect == 0f && image == null) { @@ -5656,7 +5666,7 @@ internal static void DrawTextureTransparentInternal(Rect position, Texture image DrawTransparencyCheckerTexture(position, scaleMode, imageAspect); if (image != null) - DrawPreviewTexture(position, image, transparentMaterial, scaleMode, imageAspect, mipLevel); + DrawPreviewTexture(position, image, transparentMaterial, scaleMode, imageAspect, mipLevel, colorWriteMask); } internal static void DrawTransparencyCheckerTexture(Rect position, ScaleMode scaleMode, float imageAspect) @@ -5678,15 +5688,27 @@ internal static void DrawTransparencyCheckerTexture(Rect position, ScaleMode sca } // Draws the texture within a rectangle. - internal static void DrawPreviewTextureInternal(Rect position, Texture image, Material mat, ScaleMode scaleMode, float imageAspect, float mipLevel) + internal static void DrawPreviewTextureInternal(Rect position, Texture image, Material mat, ScaleMode scaleMode, float imageAspect, float mipLevel, ColorWriteMask colorWriteMask) { if (Event.current.type == EventType.Repaint) { if (imageAspect == 0) imageAspect = image.width / (float)image.height; + Color colorMask = new Color(1, 1, 1, 1); + + if ((colorWriteMask & ColorWriteMask.Red) == 0) + colorMask.r = 0; + if ((colorWriteMask & ColorWriteMask.Green) == 0) + colorMask.g = 0; + if ((colorWriteMask & ColorWriteMask.Blue) == 0) + colorMask.b = 0; + if ((colorWriteMask & ColorWriteMask.Alpha) == 0) + colorMask.a = 0; + if (mat == null) mat = GetMaterialForSpecialTexture(image, colorMaterial); + mat.SetColor("_ColorMask", colorMask); mat.SetFloat("_Mip", mipLevel); RenderTexture rt = image as RenderTexture; @@ -6343,9 +6365,9 @@ public static void DrawTextureAlpha(Rect position, Texture image, ScaleMode scal } // Draws texture transparently using the alpha channel. - public static void DrawTextureTransparent(Rect position, Texture image, [DefaultValue("ScaleMode.StretchToFill")] ScaleMode scaleMode, [DefaultValue("0")] float imageAspect, [DefaultValue("-1")] float mipLevel) + public static void DrawTextureTransparent(Rect position, Texture image, [DefaultValue("ScaleMode.StretchToFill")] ScaleMode scaleMode, [DefaultValue("0")] float imageAspect, [DefaultValue("-1")] float mipLevel, [DefaultValue("ColorWriteMask.All")] ColorWriteMask colorWriteMask) { - DrawTextureTransparentInternal(position, image, scaleMode, imageAspect, mipLevel); + DrawTextureTransparentInternal(position, image, scaleMode, imageAspect, mipLevel, colorWriteMask); } [ExcludeFromDocs] @@ -6360,16 +6382,35 @@ public static void DrawTextureTransparent(Rect position, Texture image) DrawTextureTransparent(position, image, ScaleMode.StretchToFill, 0); } + [ExcludeFromDocs] public static void DrawTextureTransparent(Rect position, Texture image, ScaleMode scaleMode, float imageAspect) { - DrawTextureTransparentInternal(position, image, scaleMode, imageAspect, -1); + DrawTextureTransparent(position, image, scaleMode, imageAspect, -1); + } + + [ExcludeFromDocs] + public static void DrawTextureTransparent(Rect position, Texture image, ScaleMode scaleMode, float imageAspect, float mipLevel) + { + DrawTextureTransparent(position, image, scaleMode, imageAspect, mipLevel, ColorWriteMask.All); } // Draws the texture within a rectangle. public static void DrawPreviewTexture(Rect position, Texture image, [DefaultValue("null")] Material mat, [DefaultValue("ScaleMode.StretchToFill")] ScaleMode scaleMode, - [DefaultValue("0")] float imageAspect, [DefaultValue("-1")] float mipLevel) + [DefaultValue("0")] float imageAspect, [DefaultValue("-1")] float mipLevel, [DefaultValue("ColorWriteMask.All")] ColorWriteMask colorWriteMask) { - DrawPreviewTextureInternal(position, image, mat, scaleMode, imageAspect, mipLevel); + DrawPreviewTextureInternal(position, image, mat, scaleMode, imageAspect, mipLevel, colorWriteMask); + } + + [ExcludeFromDocs] + public static void DrawPreviewTexture(Rect position, Texture image, Material mat, ScaleMode scaleMode, float imageAspect, float mipLevel) + { + DrawPreviewTexture(position, image, mat, scaleMode, imageAspect, mipLevel, ColorWriteMask.All); + } + + [ExcludeFromDocs] + public static void DrawPreviewTexture(Rect position, Texture image, Material mat, ScaleMode scaleMode, float imageAspect) + { + DrawPreviewTexture(position, image, mat, scaleMode, imageAspect, -1); } [ExcludeFromDocs] @@ -6390,12 +6431,6 @@ public static void DrawPreviewTexture(Rect position, Texture image) DrawPreviewTexture(position, image, null, ScaleMode.StretchToFill, 0); } - [ExcludeFromDocs] - public static void DrawPreviewTexture(Rect position, Texture image, Material mat, ScaleMode scaleMode, float imageAspect) - { - DrawPreviewTextureInternal(position, image, mat, scaleMode, imageAspect, -1); - } - [ExcludeFromDocs] public static void LabelField(Rect position, string label) { diff --git a/Editor/Mono/EditorHandles/BoundsHandle/PrimitiveBoundsHandle.cs b/Editor/Mono/EditorHandles/BoundsHandle/PrimitiveBoundsHandle.cs index f10253d950..7f9efafb9a 100644 --- a/Editor/Mono/EditorHandles/BoundsHandle/PrimitiveBoundsHandle.cs +++ b/Editor/Mono/EditorHandles/BoundsHandle/PrimitiveBoundsHandle.cs @@ -122,8 +122,8 @@ public void DrawHandle() // handles int prevHotControl = GUIUtility.hotControl; - Vector3 cameraLocalPos = Handles.inverseMatrix.MultiplyPoint(Camera.current.transform.position); - bool isCameraInsideBox = m_Bounds.Contains(cameraLocalPos); + bool isCameraInsideBox = Camera.current != null + && m_Bounds.Contains(Handles.inverseMatrix.MultiplyPoint(Camera.current.transform.position)); EditorGUI.BeginChangeCheck(); using (new Handles.DrawingScope(Handles.color * handleColor)) MidpointHandles(ref minPos, ref maxPos, isCameraInsideBox); diff --git a/Editor/Mono/EditorHandles/FreeMove.cs b/Editor/Mono/EditorHandles/FreeMove.cs index e360b44307..c173d409cd 100644 --- a/Editor/Mono/EditorHandles/FreeMove.cs +++ b/Editor/Mono/EditorHandles/FreeMove.cs @@ -22,7 +22,7 @@ public static Vector3 Do(int id, Vector3 position, Quaternion rotation, float si Vector3 worldPosition = Handles.matrix.MultiplyPoint(position); Matrix4x4 origMatrix = Handles.matrix; - VertexSnapping.HandleKeyAndMouseMove(id); + VertexSnapping.HandleMouseMove(id); Event evt = Event.current; switch (evt.GetTypeForControl(id)) @@ -162,7 +162,7 @@ public static Vector3 Do(int id, Vector3 position, Quaternion rotation, float si Vector3 worldPosition = Handles.matrix.MultiplyPoint(position); Matrix4x4 origMatrix = Handles.matrix; - VertexSnapping.HandleKeyAndMouseMove(id); + VertexSnapping.HandleMouseMove(id); Event evt = Event.current; switch (evt.GetTypeForControl(id)) diff --git a/Editor/Mono/EditorHandles/JointAngularLimitHandle.cs b/Editor/Mono/EditorHandles/JointAngularLimitHandle.cs index 10c838a157..9bbe0c21c8 100644 --- a/Editor/Mono/EditorHandles/JointAngularLimitHandle.cs +++ b/Editor/Mono/EditorHandles/JointAngularLimitHandle.cs @@ -26,10 +26,12 @@ private static float GetSortingDistance(ArcHandle handle) Vector3 worldPosition = Handles.matrix.MultiplyPoint3x4( Quaternion.AngleAxis(handle.angle, Vector3.up) * Vector3.forward * handle.radius ); - Vector3 toHandle = worldPosition - Camera.current.transform.position; - if (Camera.current.orthographic) + Vector3 toHandle = Camera.current == null + ? worldPosition + : worldPosition - Camera.current.transform.position; + if (Camera.current == null || Camera.current.orthographic) { - Vector3 lookVector = Camera.current.transform.forward; + Vector3 lookVector = Camera.current == null ? Vector3.forward : Camera.current.transform.forward; toHandle = lookVector * Vector3.Dot(lookVector, toHandle); } return toHandle.sqrMagnitude; diff --git a/Editor/Mono/EditorHandles/PositionHandle.cs b/Editor/Mono/EditorHandles/PositionHandle.cs index 973f78afa1..3867e4613c 100644 --- a/Editor/Mono/EditorHandles/PositionHandle.cs +++ b/Editor/Mono/EditorHandles/PositionHandle.cs @@ -322,7 +322,7 @@ static Vector3 DoPositionHandle_Internal(PositionHandleIds ids, Vector3 position } } - VertexSnapping.HandleKeyAndMouseMove(ids.xyz); + VertexSnapping.HandleMouseMove(ids.xyz); if (param.ShouldShow(PositionHandleParam.Handle.XYZ) && (isHot && ids.xyz == GUIUtility.hotControl || !isHot)) { color = ToActiveColorSpace(centerColor); diff --git a/Editor/Mono/EditorHandles/VertexSnapping.cs b/Editor/Mono/EditorHandles/VertexSnapping.cs index 69f94eafa6..5bc6bf1780 100644 --- a/Editor/Mono/EditorHandles/VertexSnapping.cs +++ b/Editor/Mono/EditorHandles/VertexSnapping.cs @@ -3,7 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; -using UnityEditor; +using UnityEditor.ShortcutManagement; namespace UnityEditor { @@ -11,7 +11,27 @@ internal class VertexSnapping { private static Vector3 s_VertexSnappingOffset = Vector3.zero; - // This method handles KeyUp, KeyDown and MouseMove for doing vertex snapping. + [Shortcut("Scene View/Toggle Vertex Snapping", typeof(SceneView), "#v")] + private static void ToggleVertexSnappingViaShortcut() + { + int id = GUIUtility.hotControl; + if (Tools.vertexDragging) + DisableVertexSnapping(id); + else + EnableVertexSnapping(id); + } + + [ClutchShortcut("Scene View/Vertex Snapping", typeof(SceneView), "v")] + private static void ToggleVertexSnappingViaClutchShortcut(ShortcutArguments arguments) + { + int id = GUIUtility.hotControl; + if (arguments.state == ShortcutState.Begin) + EnableVertexSnapping(id); + else + DisableVertexSnapping(id); + } + + // This method handles MouseMove for doing vertex snapping. // To ensure correct behaviour the caller must do on it's own: // - On MouseDown and MouseUp (if event is ours to use): // HandleUtility.ignoreRaySnapObjects = null; @@ -29,52 +49,13 @@ internal class VertexSnapping // // This is not the most elegant code-reuse solution, // but still a step up from the copy-pasted code that was used before. - public static void HandleKeyAndMouseMove(int id) + public static void HandleMouseMove(int id) { - Event evt = Event.current; - switch (evt.GetTypeForControl(id)) + var evt = Event.current; + if (evt.GetTypeForControl(id) == EventType.MouseMove && Tools.vertexDragging) { - case EventType.MouseMove: - { - if (Tools.vertexDragging) - { - EnableVertexSnapping(id); - evt.Use(); - } - break; - } - case EventType.KeyDown: - { - // Vertex selection - if (!EditorGUIUtility.editingTextField && evt.keyCode == KeyCode.V) - { - // We are searching for a vertex in our selection - if (!Tools.vertexDragging && !evt.shift) - EnableVertexSnapping(id); - - evt.Use(); - } - break; - } - case EventType.KeyUp: - { - // Vertex selection - if (!EditorGUIUtility.editingTextField && evt.keyCode == KeyCode.V) - { - if (evt.shift) - Tools.vertexDragging = !Tools.vertexDragging; // toggle vertex dragging - else if (Tools.vertexDragging) - Tools.vertexDragging = false; // stop vertex dragging - - if (Tools.vertexDragging) - EnableVertexSnapping(id); - else - DisableVertexSnapping(id); - - evt.Use(); - } - break; - } + EnableVertexSnapping(id); + evt.Use(); } } diff --git a/Editor/Mono/EditorUserBuildSettings.bindings.cs b/Editor/Mono/EditorUserBuildSettings.bindings.cs index 04b6e3a25a..f7bddf49a9 100644 --- a/Editor/Mono/EditorUserBuildSettings.bindings.cs +++ b/Editor/Mono/EditorUserBuildSettings.bindings.cs @@ -626,6 +626,15 @@ public static extern bool switchNVNShaderDebugging set; } + // Enable debug validation of NVN drawcalls + public static extern bool switchNVNDrawValidation + { + [NativeMethod("GetNVNDrawValidation")] + get; + [NativeMethod("SetNVNDrawValidation")] + set; + } + // Enable linkage of the Heap inspector tool for Nintendo Switch. public static extern bool switchEnableHeapInspector { diff --git a/Editor/Mono/EditorUtility.cs b/Editor/Mono/EditorUtility.cs index b197a5a8c8..5ebdd84813 100644 --- a/Editor/Mono/EditorUtility.cs +++ b/Editor/Mono/EditorUtility.cs @@ -198,25 +198,25 @@ public static Object InstantiatePrefab(Object target) return PrefabUtility.InstantiatePrefab(target); } - [Obsolete("Use PrefabUtility.ReplacePrefab", false)] + [Obsolete("Use PrefabUtility.SaveAsPrefabAsset with a path instead.", false)] public static GameObject ReplacePrefab(GameObject go, Object targetPrefab, ReplacePrefabOptions options) { return PrefabUtility.ReplacePrefab(go, targetPrefab, options); } - [Obsolete("Use PrefabUtility.ReplacePrefab", false)] + [Obsolete("Use PrefabUtility.SaveAsPrefabAsset or PrefabUtility.SaveAsPrefabAssetAndConnect with a path instead.", false)] public static GameObject ReplacePrefab(GameObject go, Object targetPrefab) { return PrefabUtility.ReplacePrefab(go, targetPrefab, ReplacePrefabOptions.Default); } - [Obsolete("Use PrefabUtility.CreateEmptyPrefab", false)] + [Obsolete("The concept of creating a completely empty Prefab has been discontinued. You can however use PrefabUtility.SaveAsPrefabAsset with an empty GameObject.", false)] public static Object CreateEmptyPrefab(string path) { return PrefabUtility.CreateEmptyPrefab(path); } - [Obsolete("Use PrefabUtility.RevertPrefabInstance", false)] + [Obsolete("Use PrefabUtility.RevertPrefabInstance.", false)] public static bool ReconnectToLastPrefab(GameObject go) { return PrefabUtility.ReconnectToLastPrefab(go); @@ -240,7 +240,7 @@ public static GameObject FindPrefabRoot(GameObject source) return PrefabUtility.FindPrefabRoot(source); } - [Obsolete("Use PrefabUtility.ResetToPrefabState", false)] + [Obsolete("Use PrefabUtility.RevertObjectOverride.", false)] public static bool ResetToPrefabState(Object source) { return PrefabUtility.ResetToPrefabState(source); diff --git a/Editor/Mono/EditorWindow.cs b/Editor/Mono/EditorWindow.cs index 1b17962e0f..ba8446b436 100644 --- a/Editor/Mono/EditorWindow.cs +++ b/Editor/Mono/EditorWindow.cs @@ -478,8 +478,18 @@ public void ShowUtility() ShowWithMode(ShowMode.Utility); } - // Used for popup style windows. + internal void ShowTooltip() + { + ShowPopupWithMode(ShowMode.Tooltip); + } + public void ShowPopup() + { + ShowPopupWithMode(ShowMode.PopupMenu); + } + + // Used for popup style windows. + internal void ShowPopupWithMode(ShowMode mode) { if (m_Parent == null) { @@ -494,7 +504,7 @@ public void ShowPopup() cw.position = r; cw.rootView = host; MakeParentsSettingsMatchMe(); - cw.ShowPopup(); + cw.ShowPopupWithMode(mode); } } @@ -515,7 +525,7 @@ internal void ShowWithMode(ShowMode mode) cw.position = r; cw.rootView = host; MakeParentsSettingsMatchMe(); - cw.Show(mode, true, false); + cw.Show(mode, loadPosition: true, displayImmediately: false, setFocus: true); // set min/max size now that native window is not null so that it will e.g., use proper styleMask on macOS cw.SetMinMaxSizes(minSize, maxSize); @@ -531,7 +541,7 @@ public void ShowAsDropDown(Rect buttonRect, Vector2 windowSize) internal void ShowAsDropDown(Rect buttonRect, Vector2 windowSize, PopupLocation[] locationPriorityOrder) { - ShowAsDropDown(buttonRect, windowSize, locationPriorityOrder, ShowMode.PopupMenuWithKeyboardFocus); + ShowAsDropDown(buttonRect, windowSize, locationPriorityOrder, ShowMode.PopupMenu); } internal void ShowAsDropDown(Rect buttonRect, Vector2 windowSize, PopupLocation[] locationPriorityOrder, ShowMode mode) @@ -550,9 +560,9 @@ internal void ShowAsDropDown(Rect buttonRect, Vector2 windowSize, PopupLocation[ position = ShowAsDropDownFitToScreen(buttonRect, windowSize, locationPriorityOrder); // ShowWithMode() always grabs window focus so we use ShowPopup() for popup windows so PopupWindowWithoutFocus - // will work correctly (no focus when opened) - if (ContainerWindow.IsPopup(mode) && mode != ShowMode.PopupMenuWithKeyboardFocus) - ShowPopup(); + // will work correctly (no focus when opened). + if (ContainerWindow.IsPopup(mode)) + ShowPopupWithMode(mode); else ShowWithMode(mode); @@ -1058,24 +1068,19 @@ private void OnDisableINTERNAL() // Internal stuff: // Helper to show this EditorWindow - internal static void CreateNewWindowForEditorWindow(EditorWindow window, bool loadPosition, bool showImmediately) - { - CreateNewWindowForEditorWindow(window, new Vector2(window.position.x, window.position.y), loadPosition, showImmediately); - } - - internal static void CreateNewWindowForEditorWindow(EditorWindow window, Vector2 screenPosition, bool loadPosition, bool showImmediately) + internal static void CreateNewWindowForEditorWindow(EditorWindow window, bool loadPosition, bool showImmediately, bool setFocus = true) { ContainerWindow cw = ScriptableObject.CreateInstance(); SplitView sw = ScriptableObject.CreateInstance(); cw.rootView = sw; DockArea da = ScriptableObject.CreateInstance(); - da.AddTab(window); + da.AddTab(window, setFocus); sw.AddChild(da); - Rect r = window.m_Parent.borderSize.Add(new Rect(screenPosition.x, screenPosition.y, window.position.width, window.position.height)); + Rect r = window.m_Parent.borderSize.Add(window.position); cw.position = r; sw.position = new Rect(0, 0, r.width, r.height); window.MakeParentsSettingsMatchMe(); - cw.Show(ShowMode.NormalWindow, loadPosition, showImmediately); + cw.Show(ShowMode.NormalWindow, loadPosition, showImmediately, setFocus: true); //Need this, as show my change the size of the window, due to screen constraints cw.OnResize(); } diff --git a/Editor/Mono/GI/LightmapEditorSettings.bindings.cs b/Editor/Mono/GI/LightmapEditorSettings.bindings.cs index 4f841a6632..2a28803785 100644 --- a/Editor/Mono/GI/LightmapEditorSettings.bindings.cs +++ b/Editor/Mono/GI/LightmapEditorSettings.bindings.cs @@ -29,8 +29,11 @@ public enum Lightmapper [Obsolete("Use Lightmapper.ProgressiveCPU instead. (UnityUpgradable) -> UnityEditor.LightmapEditorSettings/Lightmapper.ProgressiveCPU", true)] PathTracer = 1, - // Lightmaps are baked by the Progressive lightmapper (Wintermute + OpenRL based). - ProgressiveCPU = 1 + // Lightmaps are baked by the CPU Progressive lightmapper (Wintermute + OpenRL based). + ProgressiveCPU = 1, + + // Lightmaps are baked by the GPU Progressive lightmapper (RadeonRays + OpenCL based). + ProgressiveGPU = 2 } // Which path tracer sampling scheme is used. diff --git a/Editor/Mono/GI/LightmapVisualization.bindings.cs b/Editor/Mono/GI/LightmapVisualization.bindings.cs index 95cdebbaf8..435a75b001 100644 --- a/Editor/Mono/GI/LightmapVisualization.bindings.cs +++ b/Editor/Mono/GI/LightmapVisualization.bindings.cs @@ -31,6 +31,9 @@ internal sealed partial class LightmapVisualizationUtility [StaticAccessor("VisualisationManager::Get()", StaticAccessorType.Arrow)] internal extern static bool IsBakedTextureType(GITextureType textureType); + [StaticAccessor("VisualisationManager::Get()", StaticAccessorType.Arrow)] + internal extern static bool IsAtlasTextureType(GITextureType textureType); + [StaticAccessor("VisualisationManager::Get()", StaticAccessorType.Arrow)] public extern static VisualisationGITexture[] GetRealtimeGITextures(GITextureType textureType); diff --git a/Editor/Mono/GUI/ColorPicker.cs b/Editor/Mono/GUI/ColorPicker.cs index 453514fc52..670789bce6 100644 --- a/Editor/Mono/GUI/ColorPicker.cs +++ b/Editor/Mono/GUI/ColorPicker.cs @@ -448,7 +448,7 @@ void DrawColorSpaceBox(Rect colorBoxRect, float constantValue) m_LastConstant = constantValue; m_TextureColorBoxMode = (int)m_ColorBoxMode; } - Graphics.DrawTexture(colorBoxRect, m_ColorBox, new Rect(.5f / m_ColorBox.width, .5f / m_ColorBox.height, 1 - 1f / m_ColorBox.width, 1 - 1f / m_ColorBox.height), 0, 0, 0, 0, Color.grey); + Graphics.DrawTexture(colorBoxRect, m_ColorBox, new Rect(.5f / m_ColorBox.width, .5f / m_ColorBox.height, 1 - 1f / m_ColorBox.width, 1 - 1f / m_ColorBox.height), 0, 0, 0, 0, new Color(.5f, .5f, .5f, .5f)); } static class Styles @@ -570,7 +570,7 @@ void DoColorSwatchAndEyedropper() if (GUILayout.Button(Styles.eyeDropper, GUIStyle.none, GUILayout.Width(40), GUILayout.ExpandWidth(false))) { GUIUtility.keyboardControl = 0; - EyeDropper.Start(m_Parent, false); + EyeDropper.Start(m_Parent); m_ColorBoxMode = ColorBoxMode.EyeDropper; GUIUtility.ExitGUI(); } @@ -958,6 +958,15 @@ void OnGUI() } } + // Cancel EyeDropper if we change focus. + if (m_ColorBoxMode == ColorBoxMode.EyeDropper && + Event.current.type == EventType.ExecuteCommand && + Event.current.commandName == EventCommandNames.NewKeyboardFocus) + { + EyeDropper.End(); + OnEyedropperCancelled(); + } + // Remove keyfocus when clicked outside any control if ((Event.current.type == EventType.MouseDown && Event.current.button != 1) || Event.current.type == EventType.ContextClick) { @@ -1229,17 +1238,17 @@ internal class EyeDropper : GUIView private bool m_Focused = false; private Action m_ColorPickedCallback; - public static void Start(GUIView viewToUpdate, bool stealFocus = true) + public static void Start(GUIView viewToUpdate) { - Start(viewToUpdate, null, stealFocus); + Start(viewToUpdate, null); } - public static void Start(Action colorPickedCallback, bool stealFocus = true) + public static void Start(Action colorPickedCallback) { - Start(null, colorPickedCallback, stealFocus); + Start(null, colorPickedCallback); } - static void Start(GUIView viewToUpdate, Action colorPickedCallback, bool stealFocus) + static void Start(GUIView viewToUpdate, Action colorPickedCallback) { instance.m_DelegateView = viewToUpdate; instance.m_ColorPickedCallback = colorPickedCallback; @@ -1248,15 +1257,13 @@ static void Start(GUIView viewToUpdate, Action colorPickedCallback, bool win.title = "EyeDropper"; win.hideFlags = HideFlags.DontSave; win.rootView = instance; - win.Show(ShowMode.PopupMenu, true, false); + win.Show(ShowMode.PopupMenu, loadPosition: true, displayImmediately: false, setFocus: true); instance.AddToAuxWindowList(); win.SetInvisible(); instance.SetMinMaxSizes(new Vector2(0, 0), new Vector2(kDummyWindowSize, kDummyWindowSize)); win.position = new Rect(-kDummyWindowSize / 2, -kDummyWindowSize / 2, kDummyWindowSize, kDummyWindowSize); instance.wantsMouseMove = true; instance.StealMouseCapture(); - if (stealFocus) - instance.Focus(); } public static void End() diff --git a/Editor/Mono/GUI/CreateAssetUtility.cs b/Editor/Mono/GUI/CreateAssetUtility.cs index c33f28754c..d3b75392c6 100644 --- a/Editor/Mono/GUI/CreateAssetUtility.cs +++ b/Editor/Mono/GUI/CreateAssetUtility.cs @@ -4,6 +4,7 @@ using System.IO; using UnityEditor.ProjectWindowCallback; +using UnityEditor.Utils; using UnityEngine; @@ -44,7 +45,7 @@ public Texture2D icon public string folder { - get { return Path.GetDirectoryName(m_Path); } + get { return Path.GetDirectoryName(m_Path).ConvertSeparatorsToUnity(); } } public string extension @@ -77,19 +78,23 @@ static bool IsPathDataValid(string filePath) // Selection changes when calling BeginNewAsset if it succeeds public bool BeginNewAssetCreation(int instanceID, EndNameEditAction newAssetEndAction, string filePath, Texture2D icon, string newAssetResourceFile) { + //Sanitize input + string sanitizedFilePath = filePath != null ? filePath.ConvertSeparatorsToUnity() : filePath; + string sanitizedNewAssetResourceFile = newAssetResourceFile != null ? newAssetResourceFile.ConvertSeparatorsToUnity() : newAssetResourceFile; + string uniquePath; - if (!filePath.StartsWith("assets/", System.StringComparison.CurrentCultureIgnoreCase)) + if (!sanitizedFilePath.StartsWith("assets/", System.StringComparison.CurrentCultureIgnoreCase)) { - uniquePath = AssetDatabase.GetUniquePathNameAtSelectedPath(filePath); + uniquePath = AssetDatabase.GetUniquePathNameAtSelectedPath(sanitizedFilePath); } else { - uniquePath = AssetDatabase.GenerateUniqueAssetPath(filePath); + uniquePath = AssetDatabase.GenerateUniqueAssetPath(sanitizedFilePath); } if (!IsPathDataValid(uniquePath)) { - Debug.LogErrorFormat("Invalid generated unique path '{0}' (input path '{1}')", uniquePath, filePath); + Debug.LogErrorFormat("Invalid generated unique path '{0}' (input path '{1}')", uniquePath, sanitizedFilePath); Clear(); return false; } @@ -98,7 +103,7 @@ public bool BeginNewAssetCreation(int instanceID, EndNameEditAction newAssetEndA m_Path = uniquePath; m_Icon = icon; m_EndAction = newAssetEndAction; - m_ResourceFile = newAssetResourceFile; + m_ResourceFile = sanitizedNewAssetResourceFile; // Change selection to none or instanceID Selection.activeObject = EditorUtility.InstanceIDToObject(instanceID); diff --git a/Editor/Mono/GUI/DockArea.cs b/Editor/Mono/GUI/DockArea.cs index e64688792e..fad669b4b6 100644 --- a/Editor/Mono/GUI/DockArea.cs +++ b/Editor/Mono/GUI/DockArea.cs @@ -82,14 +82,16 @@ private static class Styles public int selected { get { return m_Selected; } - set - { - if (m_Selected != value) - m_LastSelected = m_Selected; - m_Selected = value; - if (m_Selected >= 0 && m_Selected < m_Panes.Count) - actualView = m_Panes[m_Selected]; - } + set { SetSelectedPrivate(value, sendEvents: true); } + } + + private void SetSelectedPrivate(int value, bool sendEvents) + { + if (m_Selected != value) + m_LastSelected = m_Selected; + m_Selected = value; + if (m_Selected >= 0 && m_Selected < m_Panes.Count) + SetActualViewInternal(m_Panes[m_Selected], sendEvents); } public DockArea() @@ -173,16 +175,16 @@ protected override void UpdateViewMargins(EditorWindow view) style.positionType = PositionType.Absolute; } - public void AddTab(EditorWindow pane) + public void AddTab(EditorWindow pane, bool sendPaneEvents = true) { - AddTab(m_Panes.Count, pane); + AddTab(m_Panes.Count, pane, sendPaneEvents); } - public void AddTab(int idx, EditorWindow pane) + public void AddTab(int idx, EditorWindow pane, bool sendPaneEvents = true) { - DeregisterSelectedPane(true); + DeregisterSelectedPane(clearActualView: true, sendEvents: true); m_Panes.Insert(idx, pane); - selected = idx; + SetSelectedPrivate(idx, sendPaneEvents); s_GUIContents.Clear(); var sp = parent as SplitView; @@ -192,11 +194,11 @@ public void AddTab(int idx, EditorWindow pane) Repaint(); } - public void RemoveTab(EditorWindow pane) { RemoveTab(pane, true); } - public void RemoveTab(EditorWindow pane, bool killIfEmpty) + public void RemoveTab(EditorWindow pane) { RemoveTab(pane, killIfEmpty: true); } + public void RemoveTab(EditorWindow pane, bool killIfEmpty, bool sendEvents = true) { if (actualView == pane) - DeregisterSelectedPane(true); + DeregisterSelectedPane(clearActualView: true, sendEvents: sendEvents); int idx = m_Panes.IndexOf(pane); if (idx == -1) return; // Pane is not in the window @@ -222,7 +224,7 @@ public void RemoveTab(EditorWindow pane, bool killIfEmpty) pane.m_Parent = null; if (killIfEmpty) KillIfEmpty(); - RegisterSelectedPane(); + RegisterSelectedPane(sendEvents: true); } private void KillIfEmpty() @@ -279,9 +281,10 @@ public DropInfo DragOver(EditorWindow window, Vector2 mouseScreenPosition) public bool PerformDrop(EditorWindow w, DropInfo info, Vector2 screenPos) { - s_OriginalDragSource.RemoveTab(w, s_OriginalDragSource != this); + // Don't send focus events to the tab being moved + s_OriginalDragSource.RemoveTab(w, killIfEmpty: s_OriginalDragSource != this, sendEvents: false); int tabInsertIndex = s_PlaceholderPos == -1 || s_PlaceholderPos > m_Panes.Count ? m_Panes.Count : s_PlaceholderPos; - AddTab(tabInsertIndex, w); + AddTab(tabInsertIndex, w, sendPaneEvents: false); selected = tabInsertIndex; return true; } @@ -891,7 +894,9 @@ private float DragTab(Rect tabAreaRect, float scrollOffset, GUIStyle tabStyle) ResetDragVars(); - RemoveTab(w); + // The active tab that we're moving to the new window stays focused at all times. + // Do not remove focus from the tab being detached. + RemoveTab(w, killIfEmpty: true, sendEvents: false); Rect wPos = w.position; wPos.x = screenMousePos.x - wPos.width * .5f; wPos.y = screenMousePos.y - wPos.height * .5f; @@ -900,7 +905,8 @@ private float DragTab(Rect tabAreaRect, float scrollOffset, GUIStyle tabStyle) if (Application.platform == RuntimePlatform.WindowsEditor) wPos.y = Mathf.Max(InternalEditorUtility.GetBoundsOfDesktopAtPoint(screenMousePos).y, wPos.y); - EditorWindow.CreateNewWindowForEditorWindow(w, false, false); + // Don't call OnFocus on the tab when it is moved to the new window + EditorWindow.CreateNewWindowForEditorWindow(w, loadPosition: false, showImmediately: false, setFocus: false); w.position = w.m_Parent.window.FitWindowRectToScreen(wPos, true, true); diff --git a/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs b/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs index b060fcdea7..8cdfc53eb2 100644 --- a/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs +++ b/Editor/Mono/GUI/FlexibleMenu/FlexibleMenu.cs @@ -243,7 +243,7 @@ void CreateNewItemButton(Rect itemRect) SelectItem(newIndex); EditorApplication.RequestRepaintAllViews(); // We want to repaint the flexible menu (currently in modifyItemUI) }); - PopupWindow.Show(plusRect, m_ModifyItemUI, null, ShowMode.PopupMenuWithKeyboardFocus); + PopupWindow.Show(plusRect, m_ModifyItemUI); } } @@ -261,7 +261,7 @@ void EditExistingItem(Rect itemRect, int index) m_ItemProvider.Replace(index, obj); EditorApplication.RequestRepaintAllViews(); // We want to repaint the flexible menu (currently in modifyItemUI) }); - PopupWindow.Show(itemRect, m_ModifyItemUI, null, ShowMode.PopupMenuWithKeyboardFocus); + PopupWindow.Show(itemRect, m_ModifyItemUI); } void DeleteItem(int index) diff --git a/Editor/Mono/GUI/MainView.cs b/Editor/Mono/GUI/MainView.cs index 24f90a0854..1f41368a2a 100644 --- a/Editor/Mono/GUI/MainView.cs +++ b/Editor/Mono/GUI/MainView.cs @@ -56,7 +56,7 @@ public static void MakeMain() int height = Mathf.Clamp(res.height * 3 / 4, 600, 950); cw.position = new Rect(60, 20, width, height); - cw.Show(ShowMode.MainWindow, true, true); + cw.Show(ShowMode.MainWindow, loadPosition: true, displayImmediately: true, setFocus: true); cw.DisplayAllViews(); } diff --git a/Editor/Mono/GUI/PaneDragTab.cs b/Editor/Mono/GUI/PaneDragTab.cs index 935cdd2932..ce5ca74804 100644 --- a/Editor/Mono/GUI/PaneDragTab.cs +++ b/Editor/Mono/GUI/PaneDragTab.cs @@ -131,7 +131,9 @@ public void Show(Rect pixelPos, GUIContent content, Vector2 viewSize, Vector2 mo { SetWindowPos(pixelPos); } - m_Window.Show(ShowMode.NoShadow, true, false); + + // Do not steal focus from the pane + m_Window.Show(ShowMode.NoShadow, loadPosition: true, displayImmediately: false, setFocus: false); m_TargetRect = pixelPos; } diff --git a/Editor/Mono/GUI/SplitView.cs b/Editor/Mono/GUI/SplitView.cs index 2c3682beb7..f0fc43a7ff 100644 --- a/Editor/Mono/GUI/SplitView.cs +++ b/Editor/Mono/GUI/SplitView.cs @@ -445,9 +445,9 @@ public bool PerformDrop(EditorWindow dropWindow, DropInfo dropInfo, Vector2 scre parentForDrop.MakeRoomForRect(dropRect); parentForDrop.AddChild(newDockArea, dropIndex); newDockArea.position = dropRect; - DockArea.s_OriginalDragSource.RemoveTab(dropWindow); + DockArea.s_OriginalDragSource.RemoveTab(dropWindow, killIfEmpty: true, sendEvents: false); dropWindow.m_Parent = newDockArea; - newDockArea.AddTab(dropWindow); + newDockArea.AddTab(dropWindow, sendPaneEvents: false); Reflow(); RecalcMinMaxAndReflowAll(this); newDockArea.MakeVistaDWMHappyDance(); diff --git a/Editor/Mono/GUI/Toolbar.cs b/Editor/Mono/GUI/Toolbar.cs index 22e979d006..12cebecd4d 100644 --- a/Editor/Mono/GUI/Toolbar.cs +++ b/Editor/Mono/GUI/Toolbar.cs @@ -137,6 +137,7 @@ protected override void OnDisable() public static Toolbar get = null; public static bool requestShowCollabToolbar = false; + public static bool isLastShowRequestPartial = true; internal static string lastLoadedLayoutName { diff --git a/Editor/Mono/GUI/Tools/BuiltinTools.cs b/Editor/Mono/GUI/Tools/BuiltinTools.cs index 77f9d67c7f..0f11f1031b 100644 --- a/Editor/Mono/GUI/Tools/BuiltinTools.cs +++ b/Editor/Mono/GUI/Tools/BuiltinTools.cs @@ -663,7 +663,7 @@ static Vector3 MoveHandlesGUI(Rect rect, Vector3 pivot, Quaternion rotation) corners[2] = rotation * new Vector2(rect.xMax, rect.yMax) + pivot; corners[3] = rotation * new Vector2(rect.x, rect.yMax) + pivot; - VertexSnapping.HandleKeyAndMouseMove(id); + VertexSnapping.HandleMouseMove(id); bool supportsRectSnapping = Selection.transforms.Length == 1 && UnityEditorInternal.InternalEditorUtility.SupportsRectLayout(Selection.activeTransform) && diff --git a/Editor/Mono/GUI/TreeView/AssetOrGameObjectTreeViewDragging.cs b/Editor/Mono/GUI/TreeView/AssetOrGameObjectTreeViewDragging.cs index f70b67fb06..d1c9375afd 100644 --- a/Editor/Mono/GUI/TreeView/AssetOrGameObjectTreeViewDragging.cs +++ b/Editor/Mono/GUI/TreeView/AssetOrGameObjectTreeViewDragging.cs @@ -160,11 +160,6 @@ public override void StartDrag(TreeViewItem draggedItem, List draggedItemID public override DragAndDropVisualMode DoDrag(TreeViewItem parentItem, TreeViewItem targetItem, bool perform, DropPosition dropPos) { - // Scene dragging logic - DragAndDropVisualMode dragSceneResult = DoDragScenes(parentItem as GameObjectTreeViewItem, targetItem as GameObjectTreeViewItem, perform, dropPos); - if (dragSceneResult != DragAndDropVisualMode.None) - return dragSceneResult; - // Allow client to handle drag if (m_CustomDragHandling != null) { @@ -173,6 +168,11 @@ public override DragAndDropVisualMode DoDrag(TreeViewItem parentItem, TreeViewIt return dragResult; } + // Scene dragging logic + DragAndDropVisualMode dragSceneResult = DoDragScenes(parentItem as GameObjectTreeViewItem, targetItem as GameObjectTreeViewItem, perform, dropPos); + if (dragSceneResult != DragAndDropVisualMode.None) + return dragSceneResult; + if (targetItem != null && !IsDropTargetUserModifiable(targetItem as GameObjectTreeViewItem, dropPos)) return DragAndDropVisualMode.Rejected; diff --git a/Editor/Mono/GUI/TreeView/GameObjectTreeViewDataSource.cs b/Editor/Mono/GUI/TreeView/GameObjectTreeViewDataSource.cs index 26f2ba9e37..1182c65d9d 100644 --- a/Editor/Mono/GUI/TreeView/GameObjectTreeViewDataSource.cs +++ b/Editor/Mono/GUI/TreeView/GameObjectTreeViewDataSource.cs @@ -208,10 +208,6 @@ void CreateRootItem(HierarchyProperty property) // All game objects m_RootItem = new GameObjectTreeViewItem(m_RootInstanceID, rootDepth, null, "RootOfAll"); } - - // Ensure root is expanded if not shown - if (!showRootItem) - SetExpanded(m_RootItem, true); } void ClearSearchFilter() diff --git a/Editor/Mono/GUI/WindowLayout.cs b/Editor/Mono/GUI/WindowLayout.cs index 7ce55f3e21..0b3d4ea174 100644 --- a/Editor/Mono/GUI/WindowLayout.cs +++ b/Editor/Mono/GUI/WindowLayout.cs @@ -356,10 +356,13 @@ internal static void MaximizeKeyHandler(ShortcutArguments args) var mouseOverWindow = EditorWindow.mouseOverWindow; - if (IsMaximized(mouseOverWindow)) - Unmaximize(mouseOverWindow); - else - Maximize(mouseOverWindow); + if (mouseOverWindow != null) + { + if (IsMaximized(mouseOverWindow)) + Unmaximize(mouseOverWindow); + else + Maximize(mouseOverWindow); + } } public static void AddSplitViewAndChildrenRecurse(View splitview, ArrayList list) @@ -597,7 +600,7 @@ public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated throw new System.Exception(); } - mainWindow.Show(mainWindow.showMode, true, true); + mainWindow.Show(mainWindow.showMode, loadPosition: true, displayImmediately: true, setFocus: true); // Show other windows for (int i = 0; i < newWindows.Count; i++) @@ -608,7 +611,7 @@ public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated ContainerWindow containerWindow = newWindows[i] as ContainerWindow; if (containerWindow && containerWindow != mainWindow) - containerWindow.Show(containerWindow.showMode, true, true); + containerWindow.Show(containerWindow.showMode, loadPosition: true, displayImmediately: true, setFocus: true); } // Unmaximize maximized GameView if maximize on play is enabled diff --git a/Editor/Mono/GUIView.cs b/Editor/Mono/GUIView.cs index 2398aec6e2..a86b7dd95c 100644 --- a/Editor/Mono/GUIView.cs +++ b/Editor/Mono/GUIView.cs @@ -36,7 +36,6 @@ protected Panel panel { if (m_Panel == null) { - UXMLEditorFactories.RegisterAll(); m_Panel = UIElementsUtility.FindOrCreatePanel(this, ContextType.Editor, DataWatchService.sharedInstance); m_Panel.cursorManager = m_CursorManager; m_Panel.contextualMenuManager = s_ContextualMenuManager; diff --git a/Editor/Mono/GameView/GameView.cs b/Editor/Mono/GameView/GameView.cs index 628b539d89..8f0c17b335 100644 --- a/Editor/Mono/GameView/GameView.cs +++ b/Editor/Mono/GameView/GameView.cs @@ -385,6 +385,9 @@ private void UpdateZoomAreaAndParent() CopyDimensionsToParentView(); m_LastWindowPixelSize = position.size * EditorGUIUtility.pixelsPerPoint; EditorApplication.SetSceneRepaintDirty(); + + // update the scale according to new resolution + m_ZoomArea.UpdateZoomScale(maxScale, minScale); } void AllowCursorLockAndHide(bool enable) diff --git a/Editor/Mono/GameView/GameViewSizes.cs b/Editor/Mono/GameView/GameViewSizes.cs index 6a326192d2..c43dd0b614 100644 --- a/Editor/Mono/GameView/GameViewSizes.cs +++ b/Editor/Mono/GameView/GameViewSizes.cs @@ -197,8 +197,12 @@ private void InitBuiltinGroups() // iOS GameViewSize k_iPhone_750p_Portrait = new GameViewSize(GameViewSizeType.FixedResolution, 750, 1334, "iPhone 1334x750 Portrait"); GameViewSize k_iPhone_750p_Landscape = new GameViewSize(GameViewSizeType.FixedResolution, 1334, 750, "iPhone 1334x750 Landscape"); - GameViewSize k_iPhone_X_Portrait = new GameViewSize(GameViewSizeType.FixedResolution, 1125, 2436, "iPhoneX 2436x1125 Portrait"); - GameViewSize k_iPhone_X_Landscape = new GameViewSize(GameViewSizeType.FixedResolution, 2436, 1125, "iPhoneX 2436x1125 Landscape"); + GameViewSize k_iPhone_X_Portrait = new GameViewSize(GameViewSizeType.FixedResolution, 1125, 2436, "iPhone X/XS 2436x1125 Portrait"); + GameViewSize k_iPhone_X_Landscape = new GameViewSize(GameViewSizeType.FixedResolution, 2436, 1125, "iPhone X/XS 2436x1125 Landscape"); + GameViewSize k_iPhone_828p_Portrait = new GameViewSize(GameViewSizeType.FixedResolution, 828, 1792, "iPhone XR 1792x828 Portrait"); + GameViewSize k_iPhone_828p_Landscape = new GameViewSize(GameViewSizeType.FixedResolution, 1792, 828, "iPhone XR 1792x828 Landscape"); + GameViewSize k_iPhone_1242p_Portrait = new GameViewSize(GameViewSizeType.FixedResolution, 1242, 2688, "iPhone XS Max 2688x1242 Portrait"); + GameViewSize k_iPhone_1242p_Landscape = new GameViewSize(GameViewSizeType.FixedResolution, 2688, 1242, "iPhone XS Max 2688x1242 Landscape"); GameViewSize k_iPad_1536p_Landscape = new GameViewSize(GameViewSizeType.FixedResolution, 2048, 1536, "iPad 2048x1536 Landscape"); GameViewSize k_iPad_1536p_Portrait = new GameViewSize(GameViewSizeType.FixedResolution, 1536, 2048, "iPad 2048x1536 Portrait"); @@ -232,6 +236,8 @@ private void InitBuiltinGroups() k_iPhone_750p_Portrait, k_iPhone_750p_Landscape, k_1080p_Portrait, k_1080p_Landscape, k_iPhone_X_Portrait, k_iPhone_X_Landscape, + k_iPhone_828p_Portrait, k_iPhone_828p_Landscape, + k_iPhone_1242p_Portrait, k_iPhone_1242p_Landscape, k_iPad_1536p_Landscape, k_iPad_1536p_Portrait, k_iPad_2048p_Landscape, k_iPad_2048p_Portrait, k_iPad_1668p_Landscape, k_iPad_1668p_Portrait, @@ -239,9 +245,10 @@ private void InitBuiltinGroups() k_4_3_Landscape, k_4_3_Portrait, k_iPhone4_Portrait, k_iPhone4_Landscape, k_iPhone5_Portrait, k_iPhone5_Landscape, - k_iPad_768p_Landscape, k_iPad_768p_Portrait); + k_iPad_768p_Landscape, k_iPad_768p_Portrait, + m_Remote); - m_Android.AddBuiltinSizes(kFree, m_Remote, + m_Android.AddBuiltinSizes(kFree, k_800x480_Portrait, k_800x480_Landscape, k_720p_Portrait, k_720p_Landscape, k_1080p_Portrait, k_1080p_Landscape, @@ -249,7 +256,8 @@ private void InitBuiltinGroups() k_2560x1440_Portrait, k_2560x1440_Landscape, k_2960x1440_Portrait, k_2960x1440_Landscape, k_16_9_Portrait, k_16_9_Landscape, - k_18_9_Portrait, k_18_9_Landscape); + k_18_9_Portrait, k_18_9_Landscape, + m_Remote); m_HMD.AddBuiltinSizes(kFree, m_Remote); } diff --git a/Editor/Mono/HandleUtility.cs b/Editor/Mono/HandleUtility.cs index 8fc8f52881..2e87d2449c 100644 --- a/Editor/Mono/HandleUtility.cs +++ b/Editor/Mono/HandleUtility.cs @@ -26,7 +26,7 @@ public static float CalcLineTranslation(Vector2 src, Vector2 dest, Vector3 srcPo // The constrained direction is facing towards the camera, THATS BAD when the handle is close to the camera // The srcPosition goes through to the other side of the camera float invert = 1.0F; - Vector3 cameraForward = Camera.current.transform.forward; + Vector3 cameraForward = Camera.current == null ? Vector3.forward : Camera.current.transform.forward; if (Vector3.Dot(constraintDir, cameraForward) < 0.0F) invert = -1.0F; @@ -36,8 +36,13 @@ public static float CalcLineTranslation(Vector2 src, Vector2 dest, Vector3 srcPo Vector3 cd = constraintDir; cd.y = -cd.y; Camera cam = Camera.current; - Vector2 p1 = EditorGUIUtility.PixelsToPoints(cam.WorldToScreenPoint(srcPosition)); - Vector2 p2 = EditorGUIUtility.PixelsToPoints(cam.WorldToScreenPoint(srcPosition + constraintDir * invert)); + // if camera is null, then we are drawing in OnGUI, where y-coordinate goes top-to-bottom + Vector2 p1 = cam == null + ? Vector2.Scale(srcPosition, new Vector2(1f, -1f)) + : EditorGUIUtility.PixelsToPoints(cam.WorldToScreenPoint(srcPosition)); + Vector2 p2 = cam == null + ? Vector2.Scale(srcPosition + constraintDir * invert, new Vector2(1f, -1f)) + : EditorGUIUtility.PixelsToPoints(cam.WorldToScreenPoint(srcPosition + constraintDir * invert)); Vector2 p3 = dest; Vector2 p4 = src; diff --git a/Editor/Mono/Handles.cs b/Editor/Mono/Handles.cs index 4dd97995b5..db89421be6 100644 --- a/Editor/Mono/Handles.cs +++ b/Editor/Mono/Handles.cs @@ -550,8 +550,8 @@ public static void DotHandleCap(int controlID, Vector3 position, Quaternion rota // Only apply matrix to the position because DotCap is camera facing position = matrix.MultiplyPoint(position); - Vector3 sideways = Camera.current.transform.right * size; - Vector3 up = Camera.current.transform.up * size; + Vector3 sideways = (Camera.current == null ? Vector3.right : Camera.current.transform.right) * size; + Vector3 up = (Camera.current == null ? Vector3.up : Camera.current.transform.up) * size; Color col = color * new Color(1, 1, 1, 0.99f); HandleUtility.ApplyWireMaterial(Handles.zTest); diff --git a/Editor/Mono/HostView.cs b/Editor/Mono/HostView.cs index ceccdc8cc7..bcd640d654 100644 --- a/Editor/Mono/HostView.cs +++ b/Editor/Mono/HostView.cs @@ -32,22 +32,24 @@ internal class HostView : GUIView internal EditorWindow actualView { get { return m_ActualView; } - set - { - if (m_ActualView == value) - return; - DeregisterSelectedPane(true); - m_ActualView = value; - m_IsGameView = m_ActualView is GameView; - RegisterSelectedPane(); - actualViewChanged?.Invoke(this); - } + set { SetActualViewInternal(value, sendEvents: true); } + } + + internal void SetActualViewInternal(EditorWindow value, bool sendEvents) + { + if (m_ActualView == value) + return; + DeregisterSelectedPane(clearActualView: true, sendEvents: true); + m_ActualView = value; + m_IsGameView = m_ActualView is GameView; + RegisterSelectedPane(sendEvents); + actualViewChanged?.Invoke(this); } internal void ResetActiveView() { - DeregisterSelectedPane(false); - RegisterSelectedPane(); + DeregisterSelectedPane(clearActualView: false, sendEvents: true); + RegisterSelectedPane(sendEvents: true); if (actualViewChanged != null) actualViewChanged(this); } @@ -94,7 +96,7 @@ protected override void OnEnable() EditorPrefs.onValueWasUpdated += PlayModeTintColorChangedCallback; base.OnEnable(); background = null; - RegisterSelectedPane(); + RegisterSelectedPane(sendEvents: true); } protected override void OnDisable() @@ -102,7 +104,7 @@ protected override void OnDisable() EditorApplication.playModeStateChanged -= PlayModeStateChangedCallback; EditorPrefs.onValueWasUpdated -= PlayModeTintColorChangedCallback; base.OnDisable(); - DeregisterSelectedPane(false); + DeregisterSelectedPane(clearActualView: false, sendEvents: true); } protected override void OldOnGUI() @@ -159,6 +161,11 @@ internal void OnLostFocus() { EditorGUI.EndEditingActiveTextField(); Invoke("OnLostFocus"); + + // Callback could have killed us + if (!this) + return; + Repaint(); } @@ -339,7 +346,7 @@ protected void Invoke(string methodName, object obj) mi?.Invoke(obj, null); } - protected void RegisterSelectedPane() + protected void RegisterSelectedPane(bool sendEvents) { if (!m_ActualView) return; @@ -368,24 +375,27 @@ protected void RegisterSelectedPane() EditorApplication.update += m_ActualView.CheckForWindowRepaint; } - try - { - Invoke("OnBecameVisible"); - EditorModes.OnBecameVisible(m_ActualView); - Invoke("OnFocus"); - EditorModes.OnFocus(m_ActualView); - } - catch (TargetInvocationException ex) + if (sendEvents) { - // We need to catch these so the window initialization doesn't get screwed - if (ex.InnerException != null) - Debug.LogError(ex.InnerException.GetType().Name + ":" + ex.InnerException.Message); + try + { + Invoke("OnBecameVisible"); + EditorModes.OnBecameVisible(m_ActualView); + Invoke("OnFocus"); + EditorModes.OnFocus(m_ActualView); + } + catch (TargetInvocationException ex) + { + // We need to catch these so the window initialization doesn't get screwed + if (ex.InnerException != null) + Debug.LogError(ex.InnerException.GetType().Name + ":" + ex.InnerException.Message); + } } UpdateViewMargins(m_ActualView); } - protected void DeregisterSelectedPane(bool clearActualView) + protected void DeregisterSelectedPane(bool clearActualView, bool sendEvents) { if (!m_ActualView) return; @@ -417,10 +427,13 @@ protected void DeregisterSelectedPane(bool clearActualView) { EditorWindow oldActualView = m_ActualView; m_ActualView = null; - Invoke("OnLostFocus", oldActualView); - EditorModes.OnLostFocus(m_ActualView); - Invoke("OnBecameInvisible", oldActualView); - EditorModes.OnBecameInvisible(oldActualView); + if (sendEvents) + { + Invoke("OnLostFocus", oldActualView); + EditorModes.OnLostFocus(m_ActualView); + Invoke("OnBecameInvisible", oldActualView); + EditorModes.OnBecameInvisible(oldActualView); + } } } diff --git a/Editor/Mono/ImportSettings/AssetImporterEditor.cs b/Editor/Mono/ImportSettings/AssetImporterEditor.cs index d2e0537a7b..f981e73479 100644 --- a/Editor/Mono/ImportSettings/AssetImporterEditor.cs +++ b/Editor/Mono/ImportSettings/AssetImporterEditor.cs @@ -22,11 +22,13 @@ internal void InternalSetAssetImporterTargetEditor(Object editor) protected internal Object assetTarget { get { return m_AssetEditor != null ? m_AssetEditor.target : null; } } protected internal SerializedObject assetSerializedObject { get { return m_AssetEditor != null ? m_AssetEditor.serializedObject : null; } } + static string s_LocalizedTitleString = L10n.Tr("{0} Import Settings"); + internal override string targetTitle { get { - return string.Format(L10n.Tr("{0} Import Settings"), m_AssetEditor == null ? string.Empty : m_AssetEditor.targetTitle); + return string.Format(s_LocalizedTitleString, m_AssetEditor == null ? string.Empty : m_AssetEditor.targetTitle); } } diff --git a/Editor/Mono/ImportSettings/DesktopPluginImporterExtension.cs b/Editor/Mono/ImportSettings/DesktopPluginImporterExtension.cs index b2e66d701a..2264c4dac2 100644 --- a/Editor/Mono/ImportSettings/DesktopPluginImporterExtension.cs +++ b/Editor/Mono/ImportSettings/DesktopPluginImporterExtension.cs @@ -49,7 +49,7 @@ internal override void OnGUI(PluginImporterInspector inspector) // This toggle controls two things: // * Is platform enabled/disabled? // * Platform CPU value - bool isTargetEnabled = EditorGUILayout.Toggle(name, IsTargetEnabled(inspector)); + bool isTargetEnabled = EditorGUILayout.Toggle(name, IsTargetEnabled(inspector) && value.ToString() == defaultValue.ToString()); if (EditorGUI.EndChangeCheck()) { value = isTargetEnabled ? defaultValue : DesktopPluginCPUArchitecture.None; diff --git a/Editor/Mono/Inspector/AnimationClipEditor.cs b/Editor/Mono/Inspector/AnimationClipEditor.cs index 23fc6cffb2..93bbbb54b9 100644 --- a/Editor/Mono/Inspector/AnimationClipEditor.cs +++ b/Editor/Mono/Inspector/AnimationClipEditor.cs @@ -217,6 +217,7 @@ public bool needsToGenerateClipInfo const int kPosition = 3; Vector2[][][] m_QualityCurves = new Vector2[4][][]; bool m_DirtyQualityCurves = false; + bool m_FirstInitialization = true; private void InitController() { @@ -269,7 +270,12 @@ private void InitController() { m_AvatarPreview.Animator.Play(0, 0, 0); m_AvatarPreview.Animator.Update(0); - m_AvatarPreview.ResetPreviewFocus(); + + if (m_FirstInitialization) + { + m_AvatarPreview.ResetPreviewFocus(); + m_FirstInitialization = false; + } } } } diff --git a/Editor/Mono/Inspector/AudioClipInspector.cs b/Editor/Mono/Inspector/AudioClipInspector.cs index 1ac6c24213..d4304d9698 100644 --- a/Editor/Mono/Inspector/AudioClipInspector.cs +++ b/Editor/Mono/Inspector/AudioClipInspector.cs @@ -120,29 +120,26 @@ public override void OnPreviewSettings() AudioClip clip = target as AudioClip; + bool isEditingMultipleObjects = targets.Length > 1; + using (new EditorGUI.DisabledScope(AudioUtil.IsMovieAudio(clip))) { - bool isEditingMultipleObjects = targets.Length > 1; - - using (new EditorGUI.DisabledScope(isEditingMultipleObjects)) + bool oldAutoPlay = m_bAutoPlay; + bool newAutoPlay = PreviewGUI.CycleButton(oldAutoPlay ? 1 : 0, s_AutoPlayIcons) != 0; + if (oldAutoPlay != newAutoPlay) { - bool oldAutoPlay = isEditingMultipleObjects ? false : m_bAutoPlay; - bool newAutoPlay = PreviewGUI.CycleButton(oldAutoPlay ? 1 : 0, s_AutoPlayIcons) != 0; - if (oldAutoPlay != newAutoPlay) - { - m_bAutoPlay = newAutoPlay; - InspectorWindow.RepaintAllInspectors(); - } + m_bAutoPlay = newAutoPlay; + InspectorWindow.RepaintAllInspectors(); + } - bool oldLoop = isEditingMultipleObjects ? false : m_bLoop; - bool newLoop = PreviewGUI.CycleButton(oldLoop ? 1 : 0, s_LoopIcons) != 0; - if (oldLoop != newLoop) - { - m_bLoop = newLoop; - if (playing) - AudioUtil.LoopClip(clip, newLoop); - InspectorWindow.RepaintAllInspectors(); - } + bool oldLoop = m_bLoop; + bool newLoop = PreviewGUI.CycleButton(oldLoop ? 1 : 0, s_LoopIcons) != 0; + if (oldLoop != newLoop) + { + m_bLoop = newLoop; + if (playing) + AudioUtil.LoopClip(clip, newLoop); + InspectorWindow.RepaintAllInspectors(); } using (new EditorGUI.DisabledScope(isEditingMultipleObjects && !playing && m_PlayingInspector != this)) @@ -153,8 +150,10 @@ public override void OnPreviewSettings() if (newPlaying != curPlaying) { AudioUtil.StopAllClips(); + m_PlayingClip = null; + m_PlayingInspector = null; - if (newPlaying) + if (newPlaying && !isEditingMultipleObjects) { AudioUtil.PlayClip(clip, 0, m_bLoop); m_PlayingClip = clip; @@ -163,6 +162,21 @@ public override void OnPreviewSettings() } } } + + // autoplay start? + if (m_bAutoPlay && m_PlayingClip != clip && m_PlayingInspector == this && !isEditingMultipleObjects) + { + AudioUtil.StopAllClips(); + m_PlayingClip = null; + m_PlayingInspector = null; + + if (!isEditingMultipleObjects) + { + AudioUtil.PlayClip(clip, 0, m_bLoop); + m_PlayingClip = clip; + m_PlayingInspector = this; + } + } } // Passing in clip and importer separately as we're not completely done with the asset setup at the time we're asked to generate the preview. @@ -314,16 +328,6 @@ public override void OnPreviewGUI(Rect r, GUIStyle background) PreviewGUI.EndScrollView(); } - - // autoplay start? - if (m_bAutoPlay && m_PlayingClip != clip && m_PlayingInspector == this) - { - AudioUtil.StopAllClips(); - AudioUtil.PlayClip(clip, 0, m_bLoop); - m_PlayingClip = clip; - m_PlayingInspector = this; - } - // force update GUI if (playing) GUIView.current.Repaint(); diff --git a/Editor/Mono/Inspector/Avatar/AvatarMappingEditor.cs b/Editor/Mono/Inspector/Avatar/AvatarMappingEditor.cs index 50ceac4111..9cf9bfb61a 100644 --- a/Editor/Mono/Inspector/Avatar/AvatarMappingEditor.cs +++ b/Editor/Mono/Inspector/Avatar/AvatarMappingEditor.cs @@ -251,15 +251,27 @@ private void HandleBodyView(int bodyView) Vector2 m_FoldoutScroll = Vector2.zero; - public override void OnInspectorGUI() + private void SetupInternalProperties() + { + m_HumanBoneArray = serializedObject.FindProperty("m_HumanDescription.m_Human"); + m_Skeleton = serializedObject.FindProperty("m_HumanDescription.m_Skeleton"); + } + + private void HandleUndoPerformed() { if (Event.current.type == EventType.ValidateCommand && Event.current.commandName == EventCommandNames.UndoRedoPerformed) { + SetupInternalProperties(); + AvatarSetupTool.TransferPoseToDescription(m_Skeleton, root); for (int i = 0; i < m_Bones.Length; i++) m_Bones[i].Serialize(m_HumanBoneArray); } + } + public override void OnInspectorGUI() + { + HandleUndoPerformed(); UpdateSelectedBone(); // case 837655. GUI.keyboardControl is overriden when changing scene selection. diff --git a/Editor/Mono/Inspector/CameraEditor.cs b/Editor/Mono/Inspector/CameraEditor.cs index 1cd244ae15..81e164090d 100644 --- a/Editor/Mono/Inspector/CameraEditor.cs +++ b/Editor/Mono/Inspector/CameraEditor.cs @@ -213,7 +213,7 @@ public void DrawProjection() GUIContent content = EditorGUI.BeginProperty(EditorGUILayout.BeginHorizontal(), Styles.fieldOfView, fieldOfView); EditorGUI.BeginDisabled(projectionMatrixMode.hasMultipleDifferentValues || isPhysicalCamera && (focalLength.hasMultipleDifferentValues || sensorSize.hasMultipleDifferentValues)); EditorGUI.BeginChangeCheck(); - float fovNewValue = EditorGUILayout.Slider(content, fieldOfView.floatValue, 1f, 179f); + float fovNewValue = EditorGUILayout.Slider(content, fieldOfView.floatValue, 0.00001f, 179f); bool fovChanged = EditorGUI.EndChangeCheck(); EditorGUI.EndDisabled(); EditorGUILayout.EndHorizontal(); diff --git a/Editor/Mono/Inspector/Editor.cs b/Editor/Mono/Inspector/Editor.cs index c4f068ea2f..20bead8071 100644 --- a/Editor/Mono/Inspector/Editor.cs +++ b/Editor/Mono/Inspector/Editor.cs @@ -763,25 +763,19 @@ protected virtual bool ShouldHideOpenButton() internal virtual void OnHeaderIconGUI(Rect iconRect) { Texture2D icon = null; - if (!HasPreviewGUI()) + + // Fetch isLoadingAssetPreview to ensure that there is no situation where a preview needs a repaint because it hasn't finished loading yet. + bool isLoadingAssetPreview = AssetPreview.IsLoadingAssetPreview(target.GetInstanceID()); + icon = AssetPreview.GetAssetPreview(target); + if (!icon) { - // Fetch isLoadingAssetPreview to ensure that there is no situation where a preview needs a repaint because it hasn't finished loading yet. - bool isLoadingAssetPreview = AssetPreview.IsLoadingAssetPreview(target.GetInstanceID()); - icon = AssetPreview.GetAssetPreview(target); - if (!icon) - { - // We have a static preview it just hasn't been loaded yet. Repaint until we have it loaded. - if (isLoadingAssetPreview) - Repaint(); - icon = AssetPreview.GetMiniThumbnail(target); - } + // We have a static preview it just hasn't been loaded yet. Repaint until we have it loaded. + if (isLoadingAssetPreview) + Repaint(); + icon = AssetPreview.GetMiniThumbnail(target); } - if (HasPreviewGUI()) - // OnPreviewGUI must have all events; not just Repaint, or else the control IDs will mis-match. - OnPreviewGUI(iconRect, BaseStyles.inspectorBigInner); - else if (icon) - GUI.Label(iconRect, icon, BaseStyles.centerStyle); + GUI.Label(iconRect, icon, BaseStyles.centerStyle); } internal virtual void OnHeaderTitleGUI(Rect titleRect, string header) @@ -816,9 +810,10 @@ internal virtual void DrawHeaderHelpAndSettingsGUI(Rect r) // draw the header, and then start a new vertical block with the same style. private void DrawHeaderFromInsideHierarchy() { + var style = GUILayoutUtility.topLevel.style; EditorGUILayout.EndVertical(); OnHeaderGUI(); - EditorGUILayout.BeginVertical(GUILayoutUtility.topLevel.style); + EditorGUILayout.BeginVertical(style); } internal static Rect DrawHeaderGUI(Editor editor, string header) diff --git a/Editor/Mono/Inspector/GameObjectInspector.cs b/Editor/Mono/Inspector/GameObjectInspector.cs index bb5d9782ce..b984bc1af0 100644 --- a/Editor/Mono/Inspector/GameObjectInspector.cs +++ b/Editor/Mono/Inspector/GameObjectInspector.cs @@ -94,7 +94,6 @@ public Styles() } static Styles s_Styles; const float kIconSize = 24; - Vector2 previewDir; class PreviewData : IDisposable { @@ -130,20 +129,25 @@ public void Dispose() } Dictionary m_PreviewInstances = new Dictionary(); - + Dictionary m_PreviewCache; + Vector2 m_PreviewDir; + Rect m_PreviewRect; + bool m_PlayModeObjects; bool m_IsAsset; bool m_ImmutableSelf; bool m_ImmutableSourceAsset; - bool m_IsPrefabInstanceAnyRoot = false; - bool m_IsPrefabInstanceOutermostRoot = false; + bool m_IsDisconnected; + bool m_IsMissing; + bool m_IsPrefabInstanceAnyRoot; + bool m_IsPrefabInstanceOutermostRoot; bool m_AllOfSamePrefabType = true; public void OnEnable() { if (EditorSettings.defaultBehaviorMode == EditorBehaviorMode.Mode2D) - previewDir = new Vector2(0, 0); + m_PreviewDir = new Vector2(0, 0); else - previewDir = new Vector2(120, -20); + m_PreviewDir = new Vector2(120, -20); m_Name = serializedObject.FindProperty("m_Name"); m_IsActive = serializedObject.FindProperty("m_IsActive"); @@ -153,12 +157,20 @@ public void OnEnable() m_Icon = serializedObject.FindProperty("m_Icon"); CalculatePrefabStatus(); + + m_PreviewCache = new Dictionary(); } void CalculatePrefabStatus() { - m_IsPrefabInstanceAnyRoot = false; + m_PlayModeObjects = false; m_IsAsset = false; + m_ImmutableSelf = false; + m_ImmutableSourceAsset = false; + m_IsDisconnected = false; + m_IsMissing = false; + m_IsPrefabInstanceAnyRoot = true; + m_IsPrefabInstanceOutermostRoot = true; m_AllOfSamePrefabType = true; PrefabAssetType firstType = PrefabUtility.GetPrefabAssetType(targets[0]); PrefabInstanceStatus firstStatus = PrefabUtility.GetPrefabInstanceStatus(targets[0]); @@ -170,17 +182,23 @@ void CalculatePrefabStatus() if (type != firstType || status != firstStatus) m_AllOfSamePrefabType = false; - if (PrefabUtility.IsAnyPrefabInstanceRoot(go)) - m_IsPrefabInstanceAnyRoot = true; - if (m_IsPrefabInstanceAnyRoot) - m_IsPrefabInstanceOutermostRoot = PrefabUtility.IsOutermostPrefabInstanceRoot(go); + if (Application.IsPlaying(go)) + m_PlayModeObjects = true; + if (!PrefabUtility.IsAnyPrefabInstanceRoot(go)) + m_IsPrefabInstanceAnyRoot = false; // Conservative is false if any is false + if (!m_IsPrefabInstanceAnyRoot || !PrefabUtility.IsOutermostPrefabInstanceRoot(go)) + m_IsPrefabInstanceOutermostRoot = false; // Conservative is false if any is false if (PrefabUtility.IsPartOfPrefabAsset(go)) - m_IsAsset = true; + m_IsAsset = true; // Conservative is true if any is true if (m_IsAsset && PrefabUtility.IsPartOfImmutablePrefab(go)) - m_ImmutableSelf = true; + m_ImmutableSelf = true; // Conservative is true if any is true GameObject originalSourceOrVariant = PrefabUtility.GetOriginalSourceOrVariantRoot(go); if (originalSourceOrVariant != null && PrefabUtility.IsPartOfImmutablePrefab(originalSourceOrVariant)) - m_ImmutableSourceAsset = true; + m_ImmutableSourceAsset = true; // Conservative is true if any is true + if (PrefabUtility.IsDisconnectedFromPrefabAsset(go)) + m_IsDisconnected = true; + if (PrefabUtility.IsPrefabAssetMissing(go)) + m_IsMissing = true; } } @@ -188,7 +206,17 @@ void OnDisable() { foreach (var previewData in m_PreviewInstances.Values) previewData.Dispose(); - m_PreviewInstances.Clear(); + ClearPreviewCache(); + m_PreviewCache = null; + } + + void ClearPreviewCache() + { + foreach (var texture in m_PreviewCache.Values) + { + DestroyImmediate(texture); + } + m_PreviewCache.Clear(); } private static bool ShowMixedStaticEditorFlags(StaticEditorFlags mask) @@ -298,7 +326,7 @@ internal bool DrawInspector() // Prefab Toolbar if (EditorGUIUtility.comparisonViewMode == EditorGUIUtility.ComparisonViewMode.None) { - DoPrefabButtons(go); + DoPrefabButtons(); } serializedObject.ApplyModifiedProperties(); @@ -306,19 +334,18 @@ internal bool DrawInspector() return true; } - private void DoPrefabButtons(GameObject go) + private void DoPrefabButtons() { - // @TODO: If/when we support multi-editing of prefab/model instances, - // handle it here. Only show prefab bar if all are same type? - if (!m_IsPrefabInstanceAnyRoot) return; + if (!m_IsPrefabInstanceAnyRoot) + return; - using (new EditorGUI.DisabledScope(EditorApplication.isPlayingOrWillChangePlaymode && PrefabStageUtility.GetPrefabStage(go) == null)) + using (new EditorGUI.DisabledScope(m_PlayModeObjects)) { EditorGUILayout.BeginHorizontal(s_Styles.prefabButtonsHorizontalLayout); // Prefab information - PrefabAssetType prefabType = PrefabUtility.GetPrefabAssetType(go); - PrefabInstanceStatus instanceStatus = PrefabUtility.GetPrefabInstanceStatus(go); + PrefabAssetType singlePrefabType = PrefabUtility.GetPrefabAssetType(target); + PrefabInstanceStatus singleInstanceStatus = PrefabUtility.GetPrefabInstanceStatus(target); GUIContent prefixLabel; if (targets.Length > 1) { @@ -326,14 +353,14 @@ private void DoPrefabButtons(GameObject go) } else { - prefixLabel = s_Styles.goTypeLabel[(int)prefabType, (int)instanceStatus]; + prefixLabel = s_Styles.goTypeLabel[(int)singlePrefabType, (int)singleInstanceStatus]; } if (prefixLabel != null) { EditorGUILayout.BeginHorizontal(GUILayout.Width(kIconSize + s_Styles.tagFieldWidth)); GUILayout.FlexibleSpace(); - if (PrefabUtility.IsDisconnectedFromPrefabAsset(go) || PrefabUtility.IsPrefabAssetMissing(go)) + if (m_IsDisconnected || m_IsMissing) { GUI.contentColor = GUI.skin.GetStyle("CN StatusWarn").normal.textColor; GUILayout.Label(prefixLabel, EditorStyles.whiteLabel, GUILayout.ExpandWidth(false)); @@ -344,13 +371,11 @@ private void DoPrefabButtons(GameObject go) EditorGUILayout.EndHorizontal(); } - if (targets.Length > 1) - GUILayout.Label("Instance Management Disabled", s_Styles.instanceManagementInfo); - else + if (!m_IsMissing) { - if (!PrefabUtility.IsPrefabAssetMissing(go)) + using (new EditorGUI.DisabledScope(targets.Length > 1)) { - if (prefabType == PrefabAssetType.Model) + if (singlePrefabType == PrefabAssetType.Model) { // Open Model Prefab if (GUILayout.Button("Open", "MiniButtonLeft")) @@ -373,11 +398,15 @@ private void DoPrefabButtons(GameObject go) } } } + } - // Select prefab - if (GUILayout.Button("Select", "MiniButtonRight")) + // Select prefab + if (GUILayout.Button("Select", "MiniButtonRight")) + { + HashSet selectedAssets = new HashSet(); + for (int i = 0; i < targets.Length; i++) { - Selection.activeObject = PrefabUtility.GetOriginalSourceOrVariantRoot(target); + GameObject prefabGo = PrefabUtility.GetOriginalSourceOrVariantRoot(targets[i]); // Because of legacy prefab references we have to have this extra step // to make sure we ping the prefab asset correctly. @@ -385,25 +414,30 @@ private void DoPrefabButtons(GameObject go) // will reference prefabs as if they are serialized assets. Those references // works fine but we are not able to ping objects loaded directly from the asset // file, so we have to make sure we ping the metadata version of the prefab. - var assetPath = AssetDatabase.GetAssetPath(Selection.activeObject); - Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(assetPath); + var assetPath = AssetDatabase.GetAssetPath(prefabGo); + selectedAssets.Add((GameObject)AssetDatabase.LoadMainAssetAtPath(assetPath)); + } + Selection.objects = selectedAssets.ToArray(); + if (Selection.gameObjects.Length == 1) EditorGUIUtility.PingObject(Selection.activeObject); - } + } - // Should be EditorGUILayout.Space, except it does not have ExpandWidth set to false. - // Maybe we can change that? - GUILayoutUtility.GetRect(6, 6, GUILayout.ExpandWidth(false)); + // Should be EditorGUILayout.Space, except it does not have ExpandWidth set to false. + // Maybe we can change that? + GUILayoutUtility.GetRect(6, 6, GUILayout.ExpandWidth(false)); - // Reserve space regardless of whether the button is there or not to avoid jumps in button sizes. - Rect rect = GUILayoutUtility.GetRect(s_Styles.overridesContent, s_Styles.overridesDropdown); - if (m_IsPrefabInstanceOutermostRoot) + // Reserve space regardless of whether the button is there or not to avoid jumps in button sizes. + Rect rect = GUILayoutUtility.GetRect(s_Styles.overridesContent, s_Styles.overridesDropdown); + if (m_IsPrefabInstanceOutermostRoot) + { + if (EditorGUI.DropdownButton(rect, s_Styles.overridesContent, FocusType.Passive)) { - if (EditorGUI.DropdownButton(rect, s_Styles.overridesContent, FocusType.Passive)) - { - PopupWindow.Show(rect, new PrefabOverridesWindow(go)); - GUIUtility.ExitGUI(); - } + if (targets.Length > 1) + PopupWindow.Show(rect, new PrefabOverridesWindow(targets.Select(e => (GameObject)e).ToArray())); + else + PopupWindow.Show(rect, new PrefabOverridesWindow((GameObject)target)); + GUIUtility.ExitGUI(); } } } @@ -742,7 +776,7 @@ private void DoRenderPreview() float halfSize = Mathf.Max(bounds.extents.magnitude, 0.0001f); float distance = halfSize * 3.8f; - Quaternion rot = Quaternion.Euler(-previewDir.y, -previewDir.x, 0); + Quaternion rot = Quaternion.Euler(-m_PreviewDir.y, -m_PreviewDir.x, 0); Vector3 pos = bounds.center - rot * (Vector3.forward * distance); previewData.renderUtility.camera.transform.position = pos; @@ -784,17 +818,38 @@ public override void OnPreviewGUI(Rect r, GUIStyle background) return; } - previewDir = PreviewGUI.Drag2D(previewDir, r); + var direction = PreviewGUI.Drag2D(m_PreviewDir, r); + if (direction != m_PreviewDir) + { + // None of the preview are valid since the camera position has changed. + ClearPreviewCache(); + m_PreviewDir = direction; + } if (Event.current.type != EventType.Repaint) return; - var previewUtility = GetPreviewData().renderUtility; - previewUtility.BeginPreview(r, background); - - DoRenderPreview(); + if (m_PreviewRect != r) + { + ClearPreviewCache(); + m_PreviewRect = r; + } - previewUtility.EndAndDrawPreview(r); + var previewUtility = GetPreviewData().renderUtility; + Texture previewTexture; + if (m_PreviewCache.TryGetValue(referenceTargetIndex, out previewTexture)) + { + PreviewRenderUtility.DrawPreview(r, previewTexture); + } + else + { + previewUtility.BeginPreview(r, background); + DoRenderPreview(); + previewUtility.EndAndDrawPreview(r); + var copy = new RenderTexture(previewUtility.renderTexture); + Graphics.CopyTexture(previewUtility.renderTexture, copy); + m_PreviewCache.Add(referenceTargetIndex, copy); + } } // Handle dragging in scene view diff --git a/Editor/Mono/Inspector/InspectorWindow.cs b/Editor/Mono/Inspector/InspectorWindow.cs index 9c685ac9ae..ba33875f25 100644 --- a/Editor/Mono/Inspector/InspectorWindow.cs +++ b/Editor/Mono/Inspector/InspectorWindow.cs @@ -511,7 +511,8 @@ protected virtual void OnGUI() if (tracker.activeEditors.Length > 0) { - DrawVCSShortInfo(); + Editor assetEditor = InspectorWindowUtils.GetFirstNonImportInspectorEditor(tracker.activeEditors); + DrawVCSShortInfo(this, assetEditor); } } @@ -904,14 +905,13 @@ private void DetachPreview() GUIUtility.ExitGUI(); } - protected virtual void DrawVCSSticky(float offset) + private static void DrawVCSSticky(EditorWindow hostWindow, Editor assetEditor, float offset) { string message = ""; - Editor assetEditor = InspectorWindowUtils.GetFirstNonImportInspectorEditor(tracker.activeEditors); bool hasRemovedSticky = EditorPrefs.GetBool("vcssticky"); if (!hasRemovedSticky && !Editor.IsAppropriateFileOpenForEdit(assetEditor.target, out message)) { - var rect = new Rect(10, position.height - 94, position.width - 20, 80); + var rect = new Rect(10, hostWindow.position.height - 94, hostWindow.position.width - 20, 80); rect.y -= offset; if (Event.current.type == EventType.Repaint) { @@ -932,14 +932,13 @@ protected virtual void DrawVCSSticky(float offset) } } - private void DrawVCSShortInfo() + internal static void DrawVCSShortInfo(EditorWindow hostWindow, Editor assetEditor) { if (Provider.enabled && EditorSettings.externalVersionControl != ExternalVersionControl.Disabled && EditorSettings.externalVersionControl != ExternalVersionControl.AutoDetect && EditorSettings.externalVersionControl != ExternalVersionControl.Generic) { - Editor assetEditor = InspectorWindowUtils.GetFirstNonImportInspectorEditor(tracker.activeEditors); string assetPath = AssetDatabase.GetAssetPath(assetEditor.target); Asset asset = Provider.GetAssetByPath(assetPath); if (asset == null || !(asset.path.StartsWith("Assets") || asset.path.StartsWith("ProjectSettings"))) @@ -1005,15 +1004,15 @@ private void DrawVCSShortInfo() // TODO: Retrieve default CheckoutMode from VC settings (depends on asset type; native vs. imported) Task task = Provider.Checkout(assetEditor.targets, CheckoutMode.Both); task.Wait(); - Repaint(); + hostWindow.Repaint(); } } - DrawVCSSticky(rect.height / 2); + DrawVCSSticky(hostWindow, assetEditor, rect.height / 2); } } } - protected string BuildTooltip(Asset asset, Asset metaAsset) + protected static string BuildTooltip(Asset asset, Asset metaAsset) { var sb = new StringBuilder(); if (asset != null) @@ -1029,7 +1028,7 @@ protected string BuildTooltip(Asset asset, Asset metaAsset) return sb.ToString(); } - protected void DrawVCSShortInfoAsset(Asset asset, string tooltip, Rect rect, Texture2D icon, string currentState) + protected static void DrawVCSShortInfoAsset(Asset asset, string tooltip, Rect rect, Texture2D icon, string currentState) { Rect overlayRect = new Rect(rect.x, rect.y, 28, 16); Rect iconRect = overlayRect; @@ -1241,7 +1240,7 @@ private void DrawEditor(Editor[] editors, int editorIndex, bool rebuildOptimized Rect contentRect = new Rect(); bool excludedClass = ModuleMetadata.GetModuleIncludeSettingForObject(target) == ModuleIncludeSetting.ForceExclude; if (excludedClass) - EditorGUILayout.HelpBox("The built-in package which implements this component type has been disabled in Package Manager. This object will be removed in play mode and from any builds you make.", MessageType.Warning); + EditorGUILayout.HelpBox("The built-in package '" + ModuleMetadata.GetExcludingModuleForObject(target) + "', which is required this component type has been disabled in Package Manager. This object will be removed in play mode and from any builds you make.", MessageType.Warning); using (new EditorGUI.DisabledScope(!editor.IsEnabled() || excludedClass)) { diff --git a/Editor/Mono/Inspector/LabelGUI.cs b/Editor/Mono/Inspector/LabelGUI.cs index c7b69c305f..7ef70781c2 100644 --- a/Editor/Mono/Inspector/LabelGUI.cs +++ b/Editor/Mono/Inspector/LabelGUI.cs @@ -189,7 +189,7 @@ public void OnLabelGUI(Object[] assets) r.x = widthProbeRect.xMax + labelButton.margin.left; if (EditorGUI.DropdownButton(r, GUIContent.none, FocusType.Passive, labelButton)) { - PopupWindow.Show(r, new PopupList(m_AssetLabels), null, ShowMode.PopupMenuWithKeyboardFocus); + PopupWindow.Show(r, new PopupList(m_AssetLabels)); } EditorGUILayout.EndHorizontal(); @@ -209,7 +209,7 @@ private void DrawLabelList(bool partiallySelected, float xMax) { evt.Use(); rt.x = xMax; - PopupWindow.Show(rt, new PopupList(m_AssetLabels, content.text), null, ShowMode.PopupMenuWithKeyboardFocus); + PopupWindow.Show(rt, new PopupList(m_AssetLabels, content.text)); } } } diff --git a/Editor/Mono/Inspector/LightingSettingsInspector.cs b/Editor/Mono/Inspector/LightingSettingsInspector.cs index afe116b735..0f27dbf8aa 100644 --- a/Editor/Mono/Inspector/LightingSettingsInspector.cs +++ b/Editor/Mono/Inspector/LightingSettingsInspector.cs @@ -56,18 +56,25 @@ static class Styles public static readonly GUIContent CastShadows = EditorGUIUtility.TrTextContent("Cast Shadows", "Specifies whether a geometry creates shadows or not when a shadow-casting Light shines on it."); public static readonly GUIContent ReceiveShadows = EditorGUIUtility.TrTextContent("Receive Shadows", "When enabled, any shadows cast from other objects are drawn on the geometry."); public static readonly GUIContent MotionVectors = EditorGUIUtility.TrTextContent("Motion Vectors", "Specifies whether the Mesh renders 'Per Object Motion', 'Camera Motion', or 'No Motion' vectors to the Camera Motion Vector Texture."); - public static readonly GUIContent LightmapInfoBox = EditorGUIUtility.TrTextContent("To enable generation of lightmaps for this Mesh Renderer, please enable the 'Lightmap Static' property."); public static readonly GUIContent TerrainLightmapInfoBox = EditorGUIUtility.TrTextContent("To enable generation of lightmaps for this Mesh Renderer, please enable the 'Lightmap Static' property."); public static readonly GUIContent ResolutionTooHighWarning = EditorGUIUtility.TrTextContent("Precompute/indirect resolution for this terrain is probably too high. Use a lower realtime/indirect resolution setting in the Lighting window or assign LightmapParameters that use a lower resolution setting. Otherwise it may take a very long time to bake and memory consumption during and after the bake may be very high."); public static readonly GUIContent ResolutionTooLowWarning = EditorGUIUtility.TrTextContent("Precompute/indirect resolution for this terrain is probably too low. If the Clustering stage takes a long time, try using a higher realtime/indirect resolution setting in the Lighting window or assign LightmapParameters that use a higher resolution setting."); public static readonly GUIContent GINotEnabledInfo = EditorGUIUtility.TrTextContent("Lightmapping settings are currently disabled. Enable Baked Global Illumination or Realtime Global Illumination to display these settings."); + public static readonly GUIContent CastShadowsProgressiveGPUWarning = EditorGUIUtility.TrTextContent("Cast Shadows is forced to 'On' when using the GPU lightmapper (Preview), it will be supported in a later version. Use the CPU lightmapper instead if you need this functionality."); + public static readonly GUIContent ReceiveShadowsProgressiveGPUWarning = EditorGUIUtility.TrTextContent("Receive Shadows is forced to 'On' when using the GPU lightmapper (Preview), it will be supported in a later version. Use the CPU lightmapper instead if you need this functionality."); + public static readonly GUIContent OpenPreview = EditorGUIUtility.TrTextContent("Open Preview"); + + public static readonly GUIStyle OpenPreviewStyle = EditorStyles.objectFieldThumb.name + "LightmapPreviewOverlay"; + + public static readonly int PreviewPadding = 30; + public static readonly int PreviewWidth = 104; } bool m_ShowChartingSettings = true; bool m_ShowLightmapSettings = true; - bool m_ShowBakedLM = false; - bool m_ShowRealtimeLM = false; + bool m_ShowBakedLM = true; + bool m_ShowRealtimeLM = true; SerializedObject m_SerializedObject; SerializedObject m_GameObjectsSerializedObject; @@ -157,13 +164,25 @@ public void RenderMeshSettings(bool showLightmapSettings) m_GameObjectsSerializedObject.Update(); m_LightmapSettings.Update(); - EditorGUILayout.PropertyField(m_CastShadows, Styles.CastShadows, true); + // TODO(RadeonRays): remove scope for GPU lightmapper once the feature has been implemented. + using (new EditorGUI.DisabledScope(LightmapEditorSettings.lightmapper == LightmapEditorSettings.Lightmapper.ProgressiveGPU)) + { + EditorGUILayout.PropertyField(m_CastShadows, Styles.CastShadows, true); + } + if (LightmapEditorSettings.lightmapper == LightmapEditorSettings.Lightmapper.ProgressiveGPU) + EditorGUILayout.HelpBox(Styles.CastShadowsProgressiveGPUWarning.text, MessageType.Info); + bool isDeferredRenderingPath = SceneView.IsUsingDeferredRenderingPath(); if (SupportedRenderingFeatures.active.rendererSupportsReceiveShadows) { - using (new EditorGUI.DisabledScope(isDeferredRenderingPath)) + // TODO(RadeonRays): remove scope for GPU lightmapper once the feature has been implemented. + using (new EditorGUI.DisabledScope(isDeferredRenderingPath || LightmapEditorSettings.lightmapper == LightmapEditorSettings.Lightmapper.ProgressiveGPU)) + { EditorGUILayout.PropertyField(m_ReceiveShadows, Styles.ReceiveShadows, true); + } + if (LightmapEditorSettings.lightmapper == LightmapEditorSettings.Lightmapper.ProgressiveGPU) + EditorGUILayout.HelpBox(Styles.ReceiveShadowsProgressiveGPUWarning.text, MessageType.Info); } if (SupportedRenderingFeatures.active.rendererSupportsMotionVectors) @@ -227,13 +246,8 @@ public void RenderMeshSettings(bool showLightmapSettings) { EditorGUI.indentLevel += 1; - m_ShowBakedLM = EditorGUILayout.Foldout(m_ShowBakedLM, Styles.Atlas, true); - if (m_ShowBakedLM) - ShowAtlasGUI(m_Renderers[0].GetInstanceID()); - - m_ShowRealtimeLM = EditorGUILayout.Foldout(m_ShowRealtimeLM, Styles.RealtimeLM, true); - if (m_ShowRealtimeLM) - ShowRealtimeLMGUI(m_Renderers[0]); + ShowAtlasGUI(m_Renderers[0].GetInstanceID()); + ShowRealtimeLMGUI(m_Renderers[0]); EditorGUI.indentLevel -= 1; } @@ -299,13 +313,8 @@ public void RenderTerrainSettings() if (GUI.enabled && m_Terrains.Length == 1 && m_Terrains[0].terrainData != null) ShowBakePerformanceWarning(m_Terrains[0]); - m_ShowBakedLM = EditorGUILayout.Foldout(m_ShowBakedLM, Styles.Atlas, true); - if (m_ShowBakedLM) - ShowAtlasGUI(m_Terrains[0].GetInstanceID()); - - m_ShowRealtimeLM = EditorGUILayout.Foldout(m_ShowRealtimeLM, Styles.RealtimeLM, true); - if (m_ShowRealtimeLM) - ShowRealtimeLMGUI(m_Terrains[0]); + ShowAtlasGUI(m_Terrains[0].GetInstanceID()); + ShowRealtimeLMGUI(m_Terrains[0]); m_SerializedObject.ApplyModifiedProperties(); } @@ -400,44 +409,29 @@ void ShowAtlasGUI(int instanceID) if (m_CachedBakedTexture.texture == null) return; - EditorGUI.indentLevel += 1; - - GUILayout.BeginHorizontal(); - GUILayout.Space(30); + m_ShowBakedLM = EditorGUILayout.Foldout(m_ShowBakedLM, Styles.Atlas, true); - Rect rect = GUILayoutUtility.GetRect(100, 100, EditorStyles.objectField); + if (!m_ShowBakedLM) + return; - EditorGUI.Toggle(rect, false, EditorStyles.objectFieldThumb); + EditorGUI.indentLevel += 1; - if (rect.Contains(Event.current.mousePosition)) - { - Object actualTargetObject = m_CachedBakedTexture.texture; - Component com = actualTargetObject as Component; + GUILayout.BeginHorizontal(); - if (com) - actualTargetObject = com.gameObject; + DrawLightmapPreview(m_CachedBakedTexture.texture, false, instanceID); - if (Event.current.clickCount == 2) - LightmapPreviewWindow.CreateLightmapPreviewWindow(m_Renderers[0].GetInstanceID(), false, false); - else if (Event.current.clickCount == 1) - EditorGUI.PingObjectOrShowPreviewOnClick(actualTargetObject, GUILayoutUtility.GetLastRect()); - } + GUILayout.BeginVertical(); - if (Event.current.type == EventType.Repaint) - { - rect = EditorStyles.objectFieldThumb.padding.Remove(rect); - EditorGUI.DrawPreviewTexture(rect, m_CachedBakedTexture.texture); - } + GUILayout.Label(Styles.AtlasIndex.text + ": " + m_LightmapIndex.intValue.ToString()); + GUILayout.Label(Styles.AtlasTilingX.text + ": " + m_LightmapTilingOffsetX.floatValue.ToString()); + GUILayout.Label(Styles.AtlasTilingY.text + ": " + m_LightmapTilingOffsetY.floatValue.ToString()); + GUILayout.Label(Styles.AtlasOffsetX.text + ": " + m_LightmapTilingOffsetZ.floatValue.ToString()); + GUILayout.Label(Styles.AtlasOffsetY.text + ": " + m_LightmapTilingOffsetW.floatValue.ToString()); + GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); - EditorGUILayout.LabelField(Styles.AtlasIndex, GUIContent.Temp(m_LightmapIndex.intValue.ToString())); - EditorGUILayout.LabelField(Styles.AtlasTilingX, GUIContent.Temp(m_LightmapTilingOffsetX.floatValue.ToString())); - EditorGUILayout.LabelField(Styles.AtlasTilingY, GUIContent.Temp(m_LightmapTilingOffsetY.floatValue.ToString())); - EditorGUILayout.LabelField(Styles.AtlasOffsetX, GUIContent.Temp(m_LightmapTilingOffsetZ.floatValue.ToString())); - EditorGUILayout.LabelField(Styles.AtlasOffsetY, GUIContent.Temp(m_LightmapTilingOffsetW.floatValue.ToString())); - bool showProgressiveInfo = isPrefabAsset || (m_EnabledBakedGI.boolValue && LightmapEditorSettings.lightmapper != LightmapEditorSettings.Lightmapper.Enlighten); if (showProgressiveInfo && Unsupported.IsDeveloperMode()) @@ -454,19 +448,32 @@ void ShowAtlasGUI(int instanceID) LightmapEditorSettings.GetPVRAtlasInstanceOffset(instanceID, out atlasInstanceOffset); EditorGUILayout.LabelField(Styles.PVRAtlasInstanceOffset, GUIContent.Temp(atlasInstanceOffset.ToString())); } - EditorGUI.indentLevel -= 1; + + GUILayout.Space(5); } void ShowRealtimeLMGUI(Terrain terrain) { + Hash128 inputSystemHash; + if (terrain == null || !LightmapEditorSettings.GetInputSystemHash(terrain.GetInstanceID(), out inputSystemHash) || inputSystemHash == new Hash128()) + return; // early return since we don't have any lightmaps for it + + if (!UpdateRealtimeTexture(inputSystemHash, terrain.GetInstanceID())) + return; + + m_ShowRealtimeLM = EditorGUILayout.Foldout(m_ShowRealtimeLM, Styles.RealtimeLM, true); + + if (!m_ShowRealtimeLM) + return; + EditorGUI.indentLevel += 1; - Hash128 inputSystemHash; - if (terrain != null && LightmapEditorSettings.GetInputSystemHash(terrain.GetInstanceID(), out inputSystemHash)) - { - ShowRealtimeLightmapPreview(inputSystemHash); - } + GUILayout.BeginHorizontal(); + + DrawLightmapPreview(m_CachedRealtimeTexture.texture, true, terrain.GetInstanceID()); + + GUILayout.BeginVertical(); // Resolution of the system. int width, height; @@ -476,34 +483,56 @@ void ShowRealtimeLMGUI(Terrain terrain) var str = width.ToString() + "x" + height.ToString(); if (numChunksInX > 1 || numChunksInY > 1) str += string.Format(" ({0}x{1} chunks)", numChunksInX, numChunksInY); - EditorGUILayout.LabelField(Styles.RealtimeLMResolution, GUIContent.Temp(str)); + GUILayout.Label(Styles.RealtimeLMResolution.text + ": " + str); } + GUILayout.EndVertical(); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + EditorGUI.indentLevel -= 1; + + GUILayout.Space(5); } void ShowRealtimeLMGUI(Renderer renderer) { + Hash128 inputSystemHash; + if (renderer == null || !LightmapEditorSettings.GetInputSystemHash(renderer.GetInstanceID(), out inputSystemHash) || inputSystemHash == new Hash128()) + return; // early return since we don't have any lightmaps for it + + if (!UpdateRealtimeTexture(inputSystemHash, renderer.GetInstanceID())) + return; + + m_ShowRealtimeLM = EditorGUILayout.Foldout(m_ShowRealtimeLM, Styles.RealtimeLM, true); + + if (!m_ShowRealtimeLM) + return; + EditorGUI.indentLevel += 1; - Hash128 inputSystemHash = new Hash128(); - if (renderer != null && LightmapEditorSettings.GetInputSystemHash(renderer.GetInstanceID(), out inputSystemHash)) - { - ShowRealtimeLightmapPreview(inputSystemHash); - } + GUILayout.BeginHorizontal(); + + DrawLightmapPreview(m_CachedRealtimeTexture.texture, true, renderer.GetInstanceID()); + + GUILayout.BeginVertical(); int instWidth, instHeight; if (LightmapEditorSettings.GetInstanceResolution(renderer, out instWidth, out instHeight)) { - EditorGUILayout.LabelField(Styles.RealtimeLMInstanceResolution, GUIContent.Temp(instWidth.ToString() + "x" + instHeight.ToString())); + GUILayout.Label(Styles.RealtimeLMInstanceResolution.text + ": " + instWidth.ToString() + "x" + instHeight.ToString()); } int width, height; if (LightmapEditorSettings.GetSystemResolution(renderer, out width, out height)) { - EditorGUILayout.LabelField(Styles.RealtimeLMResolution, GUIContent.Temp(width.ToString() + "x" + height.ToString())); + GUILayout.Label(Styles.RealtimeLMResolution.text + ": " + width.ToString() + "x" + height.ToString()); } + GUILayout.EndVertical(); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + if (Unsupported.IsDeveloperMode()) { Hash128 instanceHash; @@ -522,12 +551,14 @@ void ShowRealtimeLMGUI(Renderer renderer) } EditorGUI.indentLevel -= 1; + + GUILayout.Space(5); } - void ShowRealtimeLightmapPreview(Hash128 inputSystemHash) + bool UpdateRealtimeTexture(Hash128 inputSystemHash, int instanceId) { if (inputSystemHash == new Hash128()) - return; + return false; Hash128 contentHash = LightmapVisualizationUtility.GetRealtimeGITextureHash(inputSystemHash, GITextureType.Irradiance); @@ -536,35 +567,51 @@ void ShowRealtimeLightmapPreview(Hash128 inputSystemHash) m_CachedRealtimeTexture = LightmapVisualizationUtility.GetRealtimeGITexture(inputSystemHash, GITextureType.Irradiance); if (m_CachedRealtimeTexture.texture == null) - return; + return false; - GUILayout.BeginHorizontal(); - GUILayout.Space(30); + return true; + } + + private void DrawLightmapPreview(Texture2D texture, bool realtimeLightmap, int instanceId) + { + GUILayout.Space(Styles.PreviewPadding); - Rect rect = GUILayoutUtility.GetRect(100, 100, EditorStyles.objectField); + int previewWidth = Styles.PreviewWidth - 4; // padding - EditorGUI.Toggle(rect, false, EditorStyles.objectFieldThumb); + Rect rect = GUILayoutUtility.GetRect(previewWidth, previewWidth, EditorStyles.objectField); + Rect buttonRect = new Rect(rect.xMax - 70, rect.yMax - 14, 70, 14); - if (rect.Contains(Event.current.mousePosition)) + if (Event.current.type == EventType.MouseDown) { - Object actualTargetObject = m_CachedRealtimeTexture.texture; - Component com = actualTargetObject as Component; + if ((buttonRect.Contains(Event.current.mousePosition) && Event.current.clickCount == 1) || + (rect.Contains(Event.current.mousePosition) && Event.current.clickCount == 2)) + { + LightmapPreviewWindow.CreateLightmapPreviewWindow(instanceId, realtimeLightmap, false); + } + else if (rect.Contains(Event.current.mousePosition) && Event.current.clickCount == 1) + { + Object actualTargetObject = texture; + Component com = actualTargetObject as Component; - if (com) - actualTargetObject = com.gameObject; + if (com) + actualTargetObject = com.gameObject; - if (Event.current.clickCount == 2) - LightmapPreviewWindow.CreateLightmapPreviewWindow(m_Renderers[0].GetInstanceID(), true, false); + EditorGUI.PingObjectOrShowPreviewOnClick(actualTargetObject, rect); + } } + EditorGUI.Toggle(rect, false, EditorStyles.objectFieldThumb); + if (Event.current.type == EventType.Repaint) { rect = EditorStyles.objectFieldThumb.padding.Remove(rect); - EditorGUI.DrawPreviewTexture(rect, m_CachedRealtimeTexture.texture); + EditorGUI.DrawPreviewTexture(rect, texture); + + Styles.OpenPreviewStyle.Draw(rect, Styles.OpenPreview, false, false, false, false); } - GUILayout.FlexibleSpace(); - GUILayout.EndHorizontal(); + float spacing = Mathf.Max(5.0f, EditorGUIUtility.labelWidth - Styles.PreviewPadding - Styles.PreviewWidth); + GUILayout.Space(spacing); } static bool HasNormals(Renderer renderer) diff --git a/Editor/Mono/Inspector/MaterialEditor.cs b/Editor/Mono/Inspector/MaterialEditor.cs index ad6c71cf1f..30628d6d4f 100644 --- a/Editor/Mono/Inspector/MaterialEditor.cs +++ b/Editor/Mono/Inspector/MaterialEditor.cs @@ -60,6 +60,7 @@ private static class Styles public static readonly GUIContent enableInstancingLabel = EditorGUIUtility.TrTextContent("Enable GPU Instancing"); public static readonly GUIContent doubleSidedGILabel = EditorGUIUtility.TrTextContent("Double Sided Global Illumination", "When enabled, the lightmapper accounts for both sides of the geometry when calculating Global Illumination. Backfaces are not rendered or added to lightmaps, but get treated as valid when seen from other objects. When using the Progressive Lightmapper backfaces bounce light using the same emission and albedo as frontfaces."); public static readonly GUIContent emissionLabel = EditorGUIUtility.TrTextContent("Emission"); + public static readonly GUIContent ProgressiveGPUWarning = EditorGUIUtility.TrTextContent("The Double Sided Global Illumination feature is not implemented in the Progressive GPU lightmapper yet. Use the CPU lightmapper instead if you need this functionality."); } private static readonly List s_MaterialEditors = new List(4); diff --git a/Editor/Mono/Inspector/MaterialEditorGUIHelpers.cs b/Editor/Mono/Inspector/MaterialEditorGUIHelpers.cs index 77dcd09308..6ff78840fe 100644 --- a/Editor/Mono/Inspector/MaterialEditorGUIHelpers.cs +++ b/Editor/Mono/Inspector/MaterialEditorGUIHelpers.cs @@ -163,8 +163,13 @@ public bool DoubleSidedGIField() } else { + // TODO(RadeonRays): change this to (lightmapper == Enlighten) once Double Sided GI works with GPU lightmapper. using (new EditorGUI.DisabledScope(LightmapEditorSettings.lightmapper != LightmapEditorSettings.Lightmapper.ProgressiveCPU)) EditorGUI.Toggle(r, Styles.doubleSidedGILabel, false); + if (LightmapEditorSettings.lightmapper == LightmapEditorSettings.Lightmapper.ProgressiveGPU) + { + EditorGUILayout.HelpBox(Styles.ProgressiveGPUWarning.text, MessageType.Info); + } } return false; } diff --git a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs index 2c022b45dc..370285f953 100644 --- a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs +++ b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs @@ -142,9 +142,9 @@ class SettingsContent public static readonly GUIContent appleDeveloperTeamID = EditorGUIUtility.TrTextContent("iOS Developer Team ID", "Developers can retrieve their Team ID by visiting the Apple Developer site under Account > Membership."); public static readonly GUIContent useOnDemandResources = EditorGUIUtility.TrTextContent("Use on demand resources*"); public static readonly GUIContent accelerometerFrequency = EditorGUIUtility.TrTextContent("Accelerometer Frequency*"); - public static readonly GUIContent cameraUsageDescription = EditorGUIUtility.TrTextContent("Camera Usage Description*"); - public static readonly GUIContent locationUsageDescription = EditorGUIUtility.TrTextContent("Location Usage Description*"); - public static readonly GUIContent microphoneUsageDescription = EditorGUIUtility.TrTextContent("Microphone Usage Description*"); + public static readonly GUIContent cameraUsageDescription = EditorGUIUtility.TrTextContent("Camera Usage Description*", "String shown to the user when requesting permission to use the device camera. Written to the NSCameraUsageDescription field in Xcode project's info.plist file"); + public static readonly GUIContent locationUsageDescription = EditorGUIUtility.TrTextContent("Location Usage Description*", "String shown to the user when requesting permission to access the device location. Written to the NSLocationWhenInUseUsageDescription field in Xcode project's info.plist file."); + public static readonly GUIContent microphoneUsageDescription = EditorGUIUtility.TrTextContent("Microphone Usage Description*", "String shown to the user when requesting to use the device microphone. Written to the NSMicrophoneUsageDescription field in Xcode project's info.plist file"); public static readonly GUIContent muteOtherAudioSources = EditorGUIUtility.TrTextContent("Mute Other Audio Sources*"); public static readonly GUIContent prepareIOSForRecording = EditorGUIUtility.TrTextContent("Prepare iOS for Recording"); public static readonly GUIContent forceIOSSpeakersWhenRecording = EditorGUIUtility.TrTextContent("Force iOS Speakers when Recording"); @@ -168,6 +168,7 @@ class SettingsContent public static readonly GUIContent managedStrippingLevel = EditorGUIUtility.TrTextContent("Managed Stripping Level", "If scripting backend is IL2CPP, managed stripping can't be disabled."); public static readonly GUIContent il2cppCompilerConfiguration = EditorGUIUtility.TrTextContent("C++ Compiler Configuration"); public static readonly GUIContent scriptingMono2x = EditorGUIUtility.TrTextContent("Mono"); + public static readonly GUIContent scriptingMono2xDeprecated = EditorGUIUtility.TrTextContent("Mono (Deprecated)"); public static readonly GUIContent scriptingWinRTDotNET = EditorGUIUtility.TrTextContent(".NET"); public static readonly GUIContent scriptingIL2CPP = EditorGUIUtility.TrTextContent("IL2CPP"); public static readonly GUIContent scriptingDefault = EditorGUIUtility.TrTextContent("Default"); @@ -1136,6 +1137,14 @@ void OpenGLES31OptionsGUI(BuildTargetGroup targetGroup, BuildTarget targetPlatfo void GraphicsAPIsGUIOnePlatform(BuildTargetGroup targetGroup, BuildTarget targetPlatform, string platformTitle) { + // Facebook on windows must be always DX11 + // TODO: Remove this when Facebook platform support contract is over + if (targetGroup == BuildTargetGroup.Facebook && + (targetPlatform == BuildTarget.StandaloneWindows || targetPlatform == BuildTarget.StandaloneWindows64)) + { + return; + } + GraphicsDeviceType[] availableDevices = PlayerSettings.GetSupportedGraphicsAPIs(targetPlatform); // if no devices (e.g. no platform module), or we only have one possible choice, then no // point in having any UI @@ -1697,10 +1706,10 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, BuildTargetGroup t EditorGUI.BeginChangeCheck(); LightmapEncodingQuality encodingQuality = PlayerSettings.GetLightmapEncodingQualityForPlatformGroup(targetGroup); LightmapEncodingQuality[] lightmapEncodingValues = {LightmapEncodingQuality.Normal, LightmapEncodingQuality.High}; - encodingQuality = BuildEnumPopup(SettingsContent.lightmapEncodingLabel, encodingQuality, lightmapEncodingValues, SettingsContent.lightmapEncodingNames); - if (EditorGUI.EndChangeCheck()) + LightmapEncodingQuality newEncodingQuality = BuildEnumPopup(SettingsContent.lightmapEncodingLabel, encodingQuality, lightmapEncodingValues, SettingsContent.lightmapEncodingNames); + if (EditorGUI.EndChangeCheck() && encodingQuality != newEncodingQuality) { - PlayerSettings.SetLightmapEncodingQualityForPlatformGroup(targetGroup, encodingQuality); + PlayerSettings.SetLightmapEncodingQualityForPlatformGroup(targetGroup, newEncodingQuality); Lightmapping.OnUpdateLightmapEncoding(targetGroup); @@ -1890,123 +1899,128 @@ private void OtherSectionConfigurationGUI(BuildTargetGroup targetGroup, ISetting // Configuration GUILayout.Label(SettingsContent.configurationTitle, EditorStyles.boldLabel); - // Scripting Runtime Version - var scriptingRuntimeVersions = new[] {ScriptingRuntimeVersion.Legacy, ScriptingRuntimeVersion.Latest}; - var scriptingRuntimeVersionNames = new[] {SettingsContent.scriptingRuntimeVersionLegacy, SettingsContent.scriptingRuntimeVersionLatest}; - var newScriptingRuntimeVersions = PlayerSettings.scriptingRuntimeVersion; - - if (EditorApplication.isPlaying) - { - var current = PlayerSettings.scriptingRuntimeVersion == ScriptingRuntimeVersion.Legacy ? SettingsContent.scriptingRuntimeVersionLegacy : SettingsContent.scriptingRuntimeVersionLatest; - BuildDisabledEnumPopup(current, SettingsContent.scriptingRuntimeVersion); - } - else + // scripting runtime settings in play mode are not supported + using (new EditorGUI.DisabledScope(EditorApplication.isPlaying)) { - newScriptingRuntimeVersions = BuildEnumPopup(SettingsContent.scriptingRuntimeVersion, PlayerSettings.scriptingRuntimeVersion, scriptingRuntimeVersions, scriptingRuntimeVersionNames); - } - - if (EditorApplication.scriptingRuntimeVersion == ScriptingRuntimeVersion.Legacy) - { - EditorGUILayout.HelpBox(SettingsContent.scriptingRuntimeVersionDeprecationWarning.text, MessageType.Warning); - } - - if (PlayerSettings.scriptingRuntimeVersion != EditorApplication.scriptingRuntimeVersion) - { - var activeScriptingRuntime = EditorApplication.scriptingRuntimeVersion == ScriptingRuntimeVersion.Legacy ? SettingsContent.scriptingRuntimeVersionLegacy : SettingsContent.scriptingRuntimeVersionLatest; - BuildDisabledEnumPopup(activeScriptingRuntime, SettingsContent.scriptingRuntimeVersionActive); - - EditorGUILayout.HelpBox(SettingsContent.scriptingRuntimeVersionActiveWarning.text, MessageType.Warning); - } + // Scripting Runtime Version + var scriptingRuntimeVersions = new[] {ScriptingRuntimeVersion.Legacy, ScriptingRuntimeVersion.Latest}; + var scriptingRuntimeVersionNames = new[] {SettingsContent.scriptingRuntimeVersionLegacy, SettingsContent.scriptingRuntimeVersionLatest}; + var newScriptingRuntimeVersions = PlayerSettings.scriptingRuntimeVersion; - if (newScriptingRuntimeVersions != PlayerSettings.scriptingRuntimeVersion) - { - if (newScriptingRuntimeVersions == EditorApplication.scriptingRuntimeVersion) + if (EditorApplication.isPlaying) { - PlayerSettings.scriptingRuntimeVersion = newScriptingRuntimeVersions; + var current = PlayerSettings.scriptingRuntimeVersion == ScriptingRuntimeVersion.Legacy ? SettingsContent.scriptingRuntimeVersionLegacy : SettingsContent.scriptingRuntimeVersionLatest; + BuildDisabledEnumPopup(current, SettingsContent.scriptingRuntimeVersion); } else { - var currentScriptingRuntimeVersions = PlayerSettings.scriptingRuntimeVersion; - PlayerSettings.scriptingRuntimeVersion = newScriptingRuntimeVersions; + newScriptingRuntimeVersions = BuildEnumPopup(SettingsContent.scriptingRuntimeVersion, PlayerSettings.scriptingRuntimeVersion, scriptingRuntimeVersions, scriptingRuntimeVersionNames); + } - if (!PlayerSettings.RelaunchProjectIfScriptRuntimeVersionHasChanged()) - { - PlayerSettings.scriptingRuntimeVersion = currentScriptingRuntimeVersions; - } + if (EditorApplication.scriptingRuntimeVersion == ScriptingRuntimeVersion.Legacy) + { + EditorGUILayout.HelpBox(SettingsContent.scriptingRuntimeVersionDeprecationWarning.text, MessageType.Warning); } - } - // Scripting back-end - IScriptingImplementations scripting = ModuleManager.GetScriptingImplementations(targetGroup); - bool targetGroupSupportsIl2Cpp = false; - bool currentBackendIsIl2Cpp = false; + if (PlayerSettings.scriptingRuntimeVersion != EditorApplication.scriptingRuntimeVersion) + { + var activeScriptingRuntime = EditorApplication.scriptingRuntimeVersion == ScriptingRuntimeVersion.Legacy ? SettingsContent.scriptingRuntimeVersionLegacy : SettingsContent.scriptingRuntimeVersionLatest; + BuildDisabledEnumPopup(activeScriptingRuntime, SettingsContent.scriptingRuntimeVersionActive); - if (scripting == null) - { - BuildDisabledEnumPopup(SettingsContent.scriptingDefault, SettingsContent.scriptingBackend); - } - else - { - var backends = scripting.Enabled(); + EditorGUILayout.HelpBox(SettingsContent.scriptingRuntimeVersionActiveWarning.text, MessageType.Warning); + } - foreach (var backend in backends) + if (newScriptingRuntimeVersions != PlayerSettings.scriptingRuntimeVersion) { - if (backend == ScriptingImplementation.IL2CPP) + if (newScriptingRuntimeVersions == EditorApplication.scriptingRuntimeVersion) { - targetGroupSupportsIl2Cpp = true; - break; + PlayerSettings.scriptingRuntimeVersion = newScriptingRuntimeVersions; + } + else + { + var currentScriptingRuntimeVersions = PlayerSettings.scriptingRuntimeVersion; + PlayerSettings.scriptingRuntimeVersion = newScriptingRuntimeVersions; + + if (!PlayerSettings.RelaunchProjectIfScriptRuntimeVersionHasChanged()) + { + PlayerSettings.scriptingRuntimeVersion = currentScriptingRuntimeVersions; + } } } - ScriptingImplementation currBackend = PlayerSettings.GetScriptingBackend(targetGroup); - currentBackendIsIl2Cpp = currBackend == ScriptingImplementation.IL2CPP; - ScriptingImplementation newBackend; + // Scripting back-end + IScriptingImplementations scripting = ModuleManager.GetScriptingImplementations(targetGroup); + bool targetGroupSupportsIl2Cpp = false; + bool currentBackendIsIl2Cpp = false; - if (targetGroup == BuildTargetGroup.tvOS) - { - newBackend = ScriptingImplementation.IL2CPP; - PlayerSettingsEditor.BuildDisabledEnumPopup(SettingsContent.scriptingIL2CPP, SettingsContent.scriptingBackend); - } - else if (backends.Length == 1) + if (scripting == null) { - newBackend = backends[0]; - BuildDisabledEnumPopup(GetNiceScriptingBackendName(backends[0]), SettingsContent.scriptingBackend); + BuildDisabledEnumPopup(SettingsContent.scriptingDefault, SettingsContent.scriptingBackend); } else { - newBackend = BuildEnumPopup(SettingsContent.scriptingBackend, currBackend, backends, GetNiceScriptingBackendNames(backends)); - } + var backends = scripting.Enabled(); - if (targetGroup == BuildTargetGroup.iOS && newBackend == ScriptingImplementation.Mono2x) - { - EditorGUILayout.HelpBox(SettingsContent.monoNotSupportediOS11WarningGUIContent.text, MessageType.Warning); - } + foreach (var backend in backends) + { + if (backend == ScriptingImplementation.IL2CPP) + { + targetGroupSupportsIl2Cpp = true; + break; + } + } - if (newBackend != currBackend) - PlayerSettings.SetScriptingBackend(targetGroup, newBackend); - } + ScriptingImplementation currBackend = PlayerSettings.GetScriptingBackend(targetGroup); + currentBackendIsIl2Cpp = currBackend == ScriptingImplementation.IL2CPP; + ScriptingImplementation newBackend; + var mono2xDeprecated = targetGroup == BuildTargetGroup.iOS; - // Api Compatibility Level - var currentCompatibilityLevel = PlayerSettings.GetApiCompatibilityLevel(targetGroup); - var availableCompatibilityLevels = GetAvailableApiCompatibilityLevels(targetGroup); + if (targetGroup == BuildTargetGroup.tvOS) + { + newBackend = ScriptingImplementation.IL2CPP; + PlayerSettingsEditor.BuildDisabledEnumPopup(SettingsContent.scriptingIL2CPP, SettingsContent.scriptingBackend); + } + else if (backends.Length == 1) + { + newBackend = backends[0]; + BuildDisabledEnumPopup(GetNiceScriptingBackendName(backends[0], mono2xDeprecated), SettingsContent.scriptingBackend); + } + else + { + newBackend = BuildEnumPopup(SettingsContent.scriptingBackend, currBackend, backends, GetNiceScriptingBackendNames(backends, mono2xDeprecated)); + } + + if (targetGroup == BuildTargetGroup.iOS && newBackend == ScriptingImplementation.Mono2x) + { + EditorGUILayout.HelpBox(SettingsContent.monoNotSupportediOS11WarningGUIContent.text, MessageType.Warning); + } + + if (newBackend != currBackend) + PlayerSettings.SetScriptingBackend(targetGroup, newBackend); + } - var newCompatibilityLevel = BuildEnumPopup(SettingsContent.apiCompatibilityLevel, currentCompatibilityLevel, availableCompatibilityLevels, GetNiceApiCompatibilityLevelNames(availableCompatibilityLevels)); + // Api Compatibility Level + var currentCompatibilityLevel = PlayerSettings.GetApiCompatibilityLevel(targetGroup); + var availableCompatibilityLevels = GetAvailableApiCompatibilityLevels(targetGroup); - if (currentCompatibilityLevel != newCompatibilityLevel) - PlayerSettings.SetApiCompatibilityLevel(targetGroup, newCompatibilityLevel); + var newCompatibilityLevel = BuildEnumPopup(SettingsContent.apiCompatibilityLevel, currentCompatibilityLevel, availableCompatibilityLevels, GetNiceApiCompatibilityLevelNames(availableCompatibilityLevels)); - if (targetGroupSupportsIl2Cpp) - { - using (new EditorGUI.DisabledScope(!currentBackendIsIl2Cpp || !scripting.AllowIL2CPPCompilerConfigurationSelection())) + if (currentCompatibilityLevel != newCompatibilityLevel) + PlayerSettings.SetApiCompatibilityLevel(targetGroup, newCompatibilityLevel); + + if (targetGroupSupportsIl2Cpp) { - var currentConfiguration = PlayerSettings.GetIl2CppCompilerConfiguration(targetGroup); - var configurations = GetIl2CppCompilerConfigurations(); - var configurationNames = GetIl2CppCompilerConfigurationNames(); + using (new EditorGUI.DisabledScope(!currentBackendIsIl2Cpp || !scripting.AllowIL2CPPCompilerConfigurationSelection())) + { + var currentConfiguration = PlayerSettings.GetIl2CppCompilerConfiguration(targetGroup); + var configurations = GetIl2CppCompilerConfigurations(); + var configurationNames = GetIl2CppCompilerConfigurationNames(); - var newConfiguration = BuildEnumPopup(SettingsContent.il2cppCompilerConfiguration, currentConfiguration, configurations, configurationNames); + var newConfiguration = BuildEnumPopup(SettingsContent.il2cppCompilerConfiguration, currentConfiguration, configurations, configurationNames); - if (currentConfiguration != newConfiguration) - PlayerSettings.SetIl2CppCompilerConfiguration(targetGroup, newConfiguration); + if (currentConfiguration != newConfiguration) + PlayerSettings.SetIl2CppCompilerConfiguration(targetGroup, newConfiguration); + } } } @@ -2313,7 +2327,6 @@ private void OtherSectionLegacyGUI() EditorGUILayout.Space(); } - private static Dictionary m_NiceScriptingBackendNames; private static Dictionary m_NiceApiCompatibilityLevelNames; private static Dictionary m_NiceManagedStrippingLevelNames; @@ -2331,31 +2344,26 @@ private static GUIContent[] GetGUIContentsForValues(Dictionary return names; } - private static GUIContent[] GetNiceScriptingBackendNames(ScriptingImplementation[] scriptingBackends) + static GUIContent[] GetNiceScriptingBackendNames(ScriptingImplementation[] scriptingBackends, bool mono2xDeprecated) { - InitializeNiceScriptingBackendNames(); - return GetGUIContentsForValues(m_NiceScriptingBackendNames, scriptingBackends); + return scriptingBackends.Select(s => GetNiceScriptingBackendName(s, mono2xDeprecated)).ToArray(); } - static void InitializeNiceScriptingBackendNames() + static GUIContent GetNiceScriptingBackendName(ScriptingImplementation scriptingBackend, bool mono2xDeprecated) { - if (m_NiceScriptingBackendNames == null) + switch (scriptingBackend) { - m_NiceScriptingBackendNames = new Dictionary - { - { ScriptingImplementation.Mono2x, SettingsContent.scriptingMono2x }, - { ScriptingImplementation.WinRTDotNET, SettingsContent.scriptingWinRTDotNET }, - { ScriptingImplementation.IL2CPP, SettingsContent.scriptingIL2CPP } - }; + case ScriptingImplementation.Mono2x: + return mono2xDeprecated ? SettingsContent.scriptingMono2xDeprecated : SettingsContent.scriptingMono2x; + case ScriptingImplementation.IL2CPP: + return SettingsContent.scriptingIL2CPP; + case ScriptingImplementation.WinRTDotNET: + return SettingsContent.scriptingWinRTDotNET; + default: + throw new ArgumentException($"Scripting backend value {scriptingBackend} is not supported.", nameof(scriptingBackend)); } } - private static GUIContent GetNiceScriptingBackendName(ScriptingImplementation scriptingBackend) - { - InitializeNiceScriptingBackendNames(); - return GetGUIContentsForValues(m_NiceScriptingBackendNames, new[] { scriptingBackend }).First(); - } - private static GUIContent[] GetNiceApiCompatibilityLevelNames(ApiCompatibilityLevel[] apiCompatibilityLevels) { if (m_NiceApiCompatibilityLevelNames == null) diff --git a/Editor/Mono/Inspector/PolygonCollider2DEditor.cs b/Editor/Mono/Inspector/PolygonCollider2DEditor.cs index c716ebf9aa..66c0156e20 100644 --- a/Editor/Mono/Inspector/PolygonCollider2DEditor.cs +++ b/Editor/Mono/Inspector/PolygonCollider2DEditor.cs @@ -91,8 +91,9 @@ private void HandleDragAndDrop(Rect targetRect) DragAndDrop.AcceptDrag(); } - return; } + + return; } DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; diff --git a/Editor/Mono/Inspector/PreviewRenderUtility.cs b/Editor/Mono/Inspector/PreviewRenderUtility.cs index 783603be1a..6d86f202c5 100644 --- a/Editor/Mono/Inspector/PreviewRenderUtility.cs +++ b/Editor/Mono/Inspector/PreviewRenderUtility.cs @@ -180,7 +180,7 @@ public Light[] lights { get { - return new[] {Light0, Light1}; + return new[] { Light0, Light1 }; } } @@ -229,7 +229,7 @@ public void BeginPreview(Rect r, GUIStyle previewBackground) new Rect(0, 0, 1, 1), previewBackground.border.left, previewBackground.border.right, previewBackground.border.top, previewBackground.border.bottom, - new Color(.5f, .5f, .5f, 1), + new Color(.5f, .5f, .5f, 0.5f), null ); @@ -341,8 +341,13 @@ private void FinishFrame() public void EndAndDrawPreview(Rect r) { - Texture t = EndPreview(); - GUI.DrawTexture(r, t, ScaleMode.StretchToFill, false); + var texture = EndPreview(); + DrawPreview(r, texture); + } + + internal static void DrawPreview(Rect r, Texture texture) + { + GUI.DrawTexture(r, texture, ScaleMode.StretchToFill, false); } public Texture2D EndStaticPreview() diff --git a/Editor/Mono/Inspector/QualitySettingsEditor.cs b/Editor/Mono/Inspector/QualitySettingsEditor.cs index d079c972d7..495fd0a4a9 100644 --- a/Editor/Mono/Inspector/QualitySettingsEditor.cs +++ b/Editor/Mono/Inspector/QualitySettingsEditor.cs @@ -503,6 +503,7 @@ public override void OnInspectorGUI() var particleRaycastBudgetProperty = currentSettings.FindPropertyRelative("particleRaycastBudget"); var asyncUploadTimeSliceProperty = currentSettings.FindPropertyRelative("asyncUploadTimeSlice"); var asyncUploadBufferSizeProperty = currentSettings.FindPropertyRelative("asyncUploadBufferSize"); + var asyncUploadPersistentBufferProperty = currentSettings.FindPropertyRelative("asyncUploadPersistentBuffer"); var resolutionScalingFixedDPIFactorProperty = currentSettings.FindPropertyRelative("resolutionScalingFixedDPIFactor"); bool usingSRP = GraphicsSettings.renderPipelineAsset != null; @@ -586,6 +587,7 @@ public override void OnInspectorGUI() EditorGUILayout.PropertyField(particleRaycastBudgetProperty); EditorGUILayout.PropertyField(asyncUploadTimeSliceProperty); EditorGUILayout.PropertyField(asyncUploadBufferSizeProperty); + EditorGUILayout.PropertyField(asyncUploadPersistentBufferProperty); asyncUploadTimeSliceProperty.intValue = Mathf.Clamp(asyncUploadTimeSliceProperty.intValue, kMinAsyncUploadTimeSlice, kMaxAsyncUploadTimeSlice); asyncUploadBufferSizeProperty.intValue = Mathf.Clamp(asyncUploadBufferSizeProperty.intValue, kMinAsyncRingBufferSize, kMaxAsyncRingBufferSize); diff --git a/Editor/Mono/Inspector/RectTransformEditor.cs b/Editor/Mono/Inspector/RectTransformEditor.cs index 3ee6543a67..a98c23be5b 100644 --- a/Editor/Mono/Inspector/RectTransformEditor.cs +++ b/Editor/Mono/Inspector/RectTransformEditor.cs @@ -298,7 +298,7 @@ void LayoutDropdownButton(bool anyWithoutParent) { GUIUtility.keyboardControl = 0; m_DropdownWindow = new LayoutDropdownWindow(serializedObject); - PopupWindow.Show(dropdownPosition, m_DropdownWindow, null, ShowMode.PopupMenuWithKeyboardFocus); + PopupWindow.Show(dropdownPosition, m_DropdownWindow); } GUI.color = oldColor; } diff --git a/Editor/Mono/Inspector/ReflectionProbeEditor.cs b/Editor/Mono/Inspector/ReflectionProbeEditor.cs index 6d4dd4faee..4d59c4b409 100644 --- a/Editor/Mono/Inspector/ReflectionProbeEditor.cs +++ b/Editor/Mono/Inspector/ReflectionProbeEditor.cs @@ -429,7 +429,7 @@ public override void OnInspectorGUI() EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = m_CustomBakedTexture.hasMultipleDifferentValues; - var newCubemap = EditorGUILayout.ObjectField(Styles.customCubemapText, m_CustomBakedTexture.objectReferenceValue, typeof(Cubemap), false); + var newCubemap = EditorGUILayout.ObjectField(Styles.customCubemapText, m_CustomBakedTexture.objectReferenceValue, typeof(Texture), false); EditorGUI.showMixedValue = false; if (EditorGUI.EndChangeCheck()) m_CustomBakedTexture.objectReferenceValue = newCubemap; diff --git a/Editor/Mono/Inspector/ShaderImporterInspector.cs b/Editor/Mono/Inspector/ShaderImporterInspector.cs index f44e2e258b..59b55ca211 100644 --- a/Editor/Mono/Inspector/ShaderImporterInspector.cs +++ b/Editor/Mono/Inspector/ShaderImporterInspector.cs @@ -214,7 +214,12 @@ internal static string[] GetShaderIncludePaths() if (s_ShaderIncludePaths == null) { List results = new List(); + // ShaderIncludePathAttribute has been obsoleted with warning but we keep it working for + // a transition period (2018.x) + // Therefore we need suppress the warning here to make the build succeed. +#pragma warning disable 618 var methods = AttributeHelper.GetMethodsWithAttribute(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly); +#pragma warning restore 618 foreach (var method in methods.methodsWithAttributes) { if (method.info.ReturnType == typeof(string[]) && method.info.GetParameters().Length == 0) diff --git a/Editor/Mono/Inspector/ShaderIncludePathAttribute.cs b/Editor/Mono/Inspector/ShaderIncludePathAttribute.cs index 965696df1c..d3887a1ef8 100644 --- a/Editor/Mono/Inspector/ShaderIncludePathAttribute.cs +++ b/Editor/Mono/Inspector/ShaderIncludePathAttribute.cs @@ -6,6 +6,7 @@ namespace UnityEditor { + [Obsolete("[ShaderIncludePath] attribute is being obsoleted. Your shader library should be under the Assets folder or in a package. To include shader headers directly from a package, use #include \"Packages//\"", false)] [AttributeUsage(AttributeTargets.Method)] public class ShaderIncludePathAttribute : Attribute { diff --git a/Editor/Mono/Inspector/ShaderInspector.cs b/Editor/Mono/Inspector/ShaderInspector.cs index 5f22865ed8..b36a6d047b 100644 --- a/Editor/Mono/Inspector/ShaderInspector.cs +++ b/Editor/Mono/Inspector/ShaderInspector.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Globalization; using Object = UnityEngine.Object; +using UnityEngine.Experimental.Rendering; namespace UnityEditor { @@ -103,6 +104,21 @@ public override void OnInspectorGUI() break; } EditorGUILayout.LabelField("Disable batching", disableBatchingString); + + // If any SRP is active, then display the SRP Batcher compatibility status + if (RenderPipelineManager.currentPipeline != null) + { + var mat = new Material(s); + mat.SetPass(0); // NOTE: Force the shader compilation to ensure GetSRPBatcherCompatibilityCode will be up to date + int subShader = ShaderUtil.GetShaderActiveSubshaderIndex(s); + int SRPErrCode = ShaderUtil.GetSRPBatcherCompatibilityCode(s, subShader); + string result = (0 == SRPErrCode) ? "compatible" : "not compatible"; + EditorGUILayout.LabelField("SRP Batcher", result); + if (SRPErrCode != 0) + { + EditorGUILayout.HelpBox(ShaderUtil.GetSRPBatcherCompatibilityIssueReason(s, subShader, SRPErrCode), MessageType.Info); + } + } ShowShaderProperties(s); } } diff --git a/Editor/Mono/Inspector/StandardParticlesShaderGUI.cs b/Editor/Mono/Inspector/StandardParticlesShaderGUI.cs index 656b97c002..760f41840b 100644 --- a/Editor/Mono/Inspector/StandardParticlesShaderGUI.cs +++ b/Editor/Mono/Inspector/StandardParticlesShaderGUI.cs @@ -54,20 +54,18 @@ private static class Styles public static GUIContent colorMode = EditorGUIUtility.TrTextContent("Color Mode", "Determines the blending mode between the particle color and the texture albedo."); public static GUIContent[] colorNames = Array.ConvertAll(Enum.GetNames(typeof(ColorMode)), item => new GUIContent(item)); - public static GUIContent flipbookMode = EditorGUIUtility.TrTextContent("Flip-Book Mode", "Determines the blending mode used for animated texture sheets."); - public static GUIContent[] flipbookNames = Array.ConvertAll(Enum.GetNames(typeof(FlipbookMode)), item => new GUIContent(item)); - + public static GUIContent flipbookBlending = EditorGUIUtility.TrTextContent("Flip-Book Frame Blending", "Enables blending between the frames of animated texture sheets."); public static GUIContent twoSidedEnabled = EditorGUIUtility.TrTextContent("Two Sided", "Render both front and back faces of the particle geometry."); - public static GUIContent distortionEnabled = EditorGUIUtility.TrTextContent("Enable Distortion", "Use a grab pass and normal map to simulate refraction."); + public static GUIContent distortionEnabled = EditorGUIUtility.TrTextContent("Distortion", "Use a grab pass and normal map to simulate refraction."); public static GUIContent distortionStrengthText = EditorGUIUtility.TrTextContent("Strength", "Distortion Strength."); public static GUIContent distortionBlendText = EditorGUIUtility.TrTextContent("Blend", "Weighting between albedo and grab pass."); - public static GUIContent softParticlesEnabled = EditorGUIUtility.TrTextContent("Enable Soft Particles", "Fade out particle geometry when it gets close to the surface of objects written into the depth buffer."); + public static GUIContent softParticlesEnabled = EditorGUIUtility.TrTextContent("Soft Particles", "Fade out particle geometry when it gets close to the surface of objects written into the depth buffer."); public static GUIContent softParticlesNearFadeDistanceText = EditorGUIUtility.TrTextContent("Near fade", "Soft Particles near fade distance."); public static GUIContent softParticlesFarFadeDistanceText = EditorGUIUtility.TrTextContent("Far fade", "Soft Particles far fade distance."); - public static GUIContent cameraFadingEnabled = EditorGUIUtility.TrTextContent("Enable Camera Fading", "Fade out particle geometry when it gets close to the camera."); + public static GUIContent cameraFadingEnabled = EditorGUIUtility.TrTextContent("Camera Fading", "Fade out particle geometry when it gets close to the camera."); public static GUIContent cameraNearFadeDistanceText = EditorGUIUtility.TrTextContent("Near fade", "Camera near fade distance."); public static GUIContent cameraFarFadeDistanceText = EditorGUIUtility.TrTextContent("Far fade", "Camera far fade distance."); @@ -184,7 +182,7 @@ public void ShaderPropertiesGUI(Material material) EditorGUILayout.Space(); GUILayout.Label(Styles.mainOptionsText, EditorStyles.boldLabel); - FlipbookModePopup(); + FlipbookBlendingPopup(); TwoSidedPopup(material); FadingPopup(material); DistortionPopup(material); @@ -296,17 +294,17 @@ void ColorModePopup() } } - void FlipbookModePopup() + void FlipbookBlendingPopup() { EditorGUI.showMixedValue = flipbookMode.hasMixedValue; - var mode = (FlipbookMode)flipbookMode.floatValue; + var enabled = (flipbookMode.floatValue == (float)FlipbookMode.Blended); EditorGUI.BeginChangeCheck(); - mode = (FlipbookMode)EditorGUILayout.Popup(Styles.flipbookMode, (int)mode, Styles.flipbookNames); + enabled = EditorGUILayout.Toggle(Styles.flipbookBlending, enabled); if (EditorGUI.EndChangeCheck()) { m_MaterialEditor.RegisterPropertyChangeUndo("Flip-Book Mode"); - flipbookMode.floatValue = (float)mode; + flipbookMode.floatValue = enabled ? (float)FlipbookMode.Blended : (float)FlipbookMode.Simple; } EditorGUI.showMixedValue = false; diff --git a/Editor/Mono/Inspector/TagManagerInspector.cs b/Editor/Mono/Inspector/TagManagerInspector.cs index 4d6d4b4f38..b4548b6da4 100644 --- a/Editor/Mono/Inspector/TagManagerInspector.cs +++ b/Editor/Mono/Inspector/TagManagerInspector.cs @@ -175,7 +175,7 @@ void NewElement(Rect buttonRect, ReorderableList list) { buttonRect.x -= 400; buttonRect.y -= 13; - PopupWindow.Show(buttonRect, new EnterNamePopup(m_Tags, s => { InternalEditorUtility.AddTag(s); }), null, ShowMode.PopupMenuWithKeyboardFocus); + PopupWindow.Show(buttonRect, new EnterNamePopup(m_Tags, s => { InternalEditorUtility.AddTag(s); })); } private void RemoveFromTagsList(ReorderableList list) diff --git a/Editor/Mono/Inspector/TextureInspector.cs b/Editor/Mono/Inspector/TextureInspector.cs index 86ba109b6e..ab99053c9b 100644 --- a/Editor/Mono/Inspector/TextureInspector.cs +++ b/Editor/Mono/Inspector/TextureInspector.cs @@ -2,8 +2,10 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System.Collections.Generic; using UnityEngine; using UnityEditor; +using UnityEngine.Rendering; namespace UnityEditor @@ -48,8 +50,9 @@ internal class TextureInspector : Editor { class Styles { - public GUIContent smallZoom, largeZoom, alphaIcon, RGBIcon; + public GUIContent smallZoom, largeZoom; public GUIStyle previewButton, previewSlider, previewSliderThumb, previewLabel; + public GUIStyle previewButtonRed, previewButtonGreen, previewButtonBlue; public readonly GUIContent wrapModeLabel = EditorGUIUtility.TrTextContent("Wrap Mode"); public readonly GUIContent wrapU = EditorGUIUtility.TrTextContent("U axis"); @@ -77,9 +80,11 @@ public Styles() { smallZoom = EditorGUIUtility.IconContent("PreTextureMipMapLow"); largeZoom = EditorGUIUtility.IconContent("PreTextureMipMapHigh"); - alphaIcon = EditorGUIUtility.IconContent("PreTextureAlpha"); - RGBIcon = EditorGUIUtility.IconContent("PreTextureRGB"); + previewButton = "preButton"; + previewButtonRed = "preButtonRed"; + previewButtonGreen = "preButtonGreen"; + previewButtonBlue = "preButtonBlue"; previewSlider = "preSlider"; previewSliderThumb = "preSliderThumb"; previewLabel = "preLabelUpper"; @@ -87,8 +92,20 @@ public Styles() } static Styles s_Styles; - private bool m_ShowAlpha; - public bool showAlpha { get { return m_ShowAlpha; } } + enum PreviewMode + { + RGB, + R, + G, + B, + A, + }; + + private PreviewMode m_PreviewMode = PreviewMode.RGB; + public bool showAlpha + { + get { return m_PreviewMode == PreviewMode.A; } + } // Plain Texture protected SerializedProperty m_WrapU; @@ -410,7 +427,6 @@ public override void OnPreviewSettings() // and while it's being shown the actual texture object might disappear -- // make sure to handle null targets. Texture tex = target as Texture; - bool showMode = true; bool alphaOnly = false; bool hasAlpha = true; int mipCount = 1; @@ -448,19 +464,53 @@ public override void OnPreviewSettings() mipCount = Mathf.Max(mipCount, TextureUtil.GetMipmapCount(t)); } + + List previewCandidates = new List(5); + previewCandidates.Add(PreviewMode.RGB); + previewCandidates.Add(PreviewMode.R); + previewCandidates.Add(PreviewMode.G); + previewCandidates.Add(PreviewMode.B); + previewCandidates.Add(PreviewMode.A); + if (alphaOnly) { - m_ShowAlpha = true; - showMode = false; + previewCandidates.Clear(); + previewCandidates.Add(PreviewMode.A); + m_PreviewMode = PreviewMode.A; } else if (!hasAlpha) { - m_ShowAlpha = false; - showMode = false; + previewCandidates.Remove(PreviewMode.A); } - if (showMode && tex != null && !IsNormalMap(tex)) - m_ShowAlpha = GUILayout.Toggle(m_ShowAlpha, m_ShowAlpha ? s_Styles.alphaIcon : s_Styles.RGBIcon, s_Styles.previewButton); + + if (previewCandidates.Count > 1 && tex != null && !IsNormalMap(tex)) + { + int selectedIndex = previewCandidates.IndexOf(m_PreviewMode); + if (selectedIndex == -1) + selectedIndex = 0; + + if (previewCandidates.Contains(PreviewMode.RGB)) + m_PreviewMode = GUILayout.Toggle(m_PreviewMode == PreviewMode.RGB, "RGB", s_Styles.previewButton) + ? PreviewMode.RGB + : m_PreviewMode; + if (previewCandidates.Contains(PreviewMode.R)) + m_PreviewMode = GUILayout.Toggle(m_PreviewMode == PreviewMode.R, "R", s_Styles.previewButtonRed) + ? PreviewMode.R + : m_PreviewMode; + if (previewCandidates.Contains(PreviewMode.G)) + m_PreviewMode = GUILayout.Toggle(m_PreviewMode == PreviewMode.G, "G", s_Styles.previewButtonGreen) + ? PreviewMode.G + : m_PreviewMode; + if (previewCandidates.Contains(PreviewMode.B)) + m_PreviewMode = GUILayout.Toggle(m_PreviewMode == PreviewMode.B, "B", s_Styles.previewButtonBlue) + ? PreviewMode.B + : m_PreviewMode; + if (previewCandidates.Contains(PreviewMode.A)) + m_PreviewMode = GUILayout.Toggle(m_PreviewMode == PreviewMode.A, "A", s_Styles.previewButton) + ? PreviewMode.A + : m_PreviewMode; + } if (mipCount > 1) { @@ -512,14 +562,33 @@ public override void OnPreviewGUI(Rect r, GUIStyle background) PreviewGUI.BeginScrollView(r, m_Pos, wantedRect, "PreHorizontalScrollbar", "PreHorizontalScrollbarThumb"); FilterMode oldFilter = t.filterMode; TextureUtil.SetFilterModeNoDirty(t, FilterMode.Point); - Texture2D t2d = t as Texture2D; - if (m_ShowAlpha) + ColorWriteMask colorWriteMask = ColorWriteMask.All; + + switch (m_PreviewMode) + { + case PreviewMode.R: + colorWriteMask = ColorWriteMask.Red | ColorWriteMask.Alpha; + break; + case PreviewMode.G: + colorWriteMask = ColorWriteMask.Green | ColorWriteMask.Alpha; + break; + case PreviewMode.B: + colorWriteMask = ColorWriteMask.Blue | ColorWriteMask.Alpha; + break; + } + + if (m_PreviewMode == PreviewMode.A) + { EditorGUI.DrawTextureAlpha(wantedRect, t, ScaleMode.StretchToFill, 0, mipLevel); - else if (t2d != null && t2d.alphaIsTransparency) - EditorGUI.DrawTextureTransparent(wantedRect, t, ScaleMode.StretchToFill, 0, mipLevel); + } else - EditorGUI.DrawPreviewTexture(wantedRect, t, null, ScaleMode.StretchToFill, 0, mipLevel); + { + if (t2d != null && t2d.alphaIsTransparency) + EditorGUI.DrawTextureTransparent(wantedRect, t, ScaleMode.StretchToFill, 0, mipLevel, colorWriteMask); + else + EditorGUI.DrawPreviewTexture(wantedRect, t, null, ScaleMode.StretchToFill, 0, mipLevel, colorWriteMask); + } // TODO: Less hacky way to prevent sprite rects to not appear in smaller previews like icons. if (wantedRect.width > 32 && wantedRect.height > 32) diff --git a/Editor/Mono/LookDevView/LookDevView.cs b/Editor/Mono/LookDevView/LookDevView.cs index ceadf35366..4709caf02b 100644 --- a/Editor/Mono/LookDevView/LookDevView.cs +++ b/Editor/Mono/LookDevView/LookDevView.cs @@ -1676,7 +1676,7 @@ private void HandleDragging() } GameObject go = o as GameObject; - if (go && EditorUtility.IsPersistent(go) && PrefabUtility.GetPrefabObject(go) != null) + if (go && EditorUtility.IsPersistent(go) && PrefabUtility.IsPartOfPrefabAsset(go)) { if (GameObjectInspector.HasRenderableParts(go)) { diff --git a/Editor/Mono/Menu.bindings.cs b/Editor/Mono/Menu.bindings.cs index aaa3b83298..c8cbee328f 100644 --- a/Editor/Mono/Menu.bindings.cs +++ b/Editor/Mono/Menu.bindings.cs @@ -16,10 +16,13 @@ public sealed class Menu [NativeMethod("MenuController::GetChecked", true)] public static extern bool GetChecked(string menuPath); - [FreeFunction("MenuController::GetMenuItemShortcuts")] - internal static extern void GetMenuItemShortcuts(List outItemNames, List outItemShortcuts); + [FreeFunction("MenuController::GetMenuItemDefaultShortcuts")] + internal static extern void GetMenuItemDefaultShortcuts(List outItemNames, List outItemDefaultShortcuts); [FreeFunction("MenuController::SetMenuItemHotkey")] internal static extern void SetHotkey(string menuPath, string hotkey); + + [FreeFunction("MenuController::ExtractSubmenus")] + internal static extern string[] ExtractSubmenus(string menuPath); } } diff --git a/Editor/Mono/PackageUtility.bindings.cs b/Editor/Mono/PackageUtility.bindings.cs index ddc81d4910..c548a47068 100644 --- a/Editor/Mono/PackageUtility.bindings.cs +++ b/Editor/Mono/PackageUtility.bindings.cs @@ -68,5 +68,8 @@ internal class PackageUtility public static extern void ImportPackageAssets(string packageName, ImportPackageItem[] items, bool performReInstall); [FreeFunction("ImportPackageAssets")] public static extern void ImportPackageAssetsImmediately(string packageName, ImportPackageItem[] items, bool performReInstall); + + [FreeFunction("TickPackageImport")] + public static extern void TickPackageImport(); } } diff --git a/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/InitialModuleUI.cs b/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/InitialModuleUI.cs index 0f20879f08..87217b8985 100644 --- a/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/InitialModuleUI.cs +++ b/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/InitialModuleUI.cs @@ -38,6 +38,7 @@ internal class InitialModuleUI : ModuleUI public SerializedProperty m_AutoRandomSeed; public SerializedProperty m_RandomSeed; public SerializedProperty m_StopAction; + public SerializedProperty m_CullingMode; public SerializedProperty m_RingBufferMode; public SerializedProperty m_RingBufferLoopRange; @@ -68,6 +69,7 @@ class Texts public GUIContent randomSeed = EditorGUIUtility.TrTextContent("Random Seed", "Randomize the look of the Particle System. Using the same seed will make the Particle System play identically each time. After changing this value, restart the Particle System to see the changes, or check the Resimulate box."); public GUIContent emitterVelocity = EditorGUIUtility.TrTextContent("Emitter Velocity", "When the Particle System is moving, should we use its Transform, or Rigidbody Component, to calculate its velocity?"); public GUIContent stopAction = EditorGUIUtility.TrTextContent("Stop Action", "When the Particle System is stopped and all particles have died, should the GameObject automatically disable/destroy itself?"); + public GUIContent cullingMode = EditorGUIUtility.TrTextContent("Culling Mode", "Choose whether to continue simulating the Particle System when offscreen. Catch-up mode pauses offscreen simulations, but performs a large simulation step when they become visible, giving the appearance that they were never paused. Automatic uses Pause mode for looping systems, and AlwaysSimulate if not looping."); public GUIContent ringBufferMode = EditorGUIUtility.TrTextContent("Ring Buffer Mode", "Rather than dying when their lifetime has elapsed, particles will remain alive until the Max Particles buffer is full, at which point new particles will replace the oldest."); public GUIContent ringBufferLoopRange = EditorGUIUtility.TrTextContent("Loop Range", "Particle lifetimes may loop between a fade-in and fade-out time, in order to use curves for the entire time they are alive. Values are in the 0-1 range."); public GUIContent x = EditorGUIUtility.TextContent("X"); @@ -96,6 +98,14 @@ class Texts EditorGUIUtility.TrTextContent("Callback") }; + public GUIContent[] cullingModes = new GUIContent[] + { + EditorGUIUtility.TrTextContent("Automatic"), + EditorGUIUtility.TrTextContent("Pause and Catch-up"), + EditorGUIUtility.TrTextContent("Pause"), + EditorGUIUtility.TrTextContent("Always Simulate") + }; + public GUIContent[] ringBufferModes = new GUIContent[] { EditorGUIUtility.TrTextContent("Disabled"), @@ -146,6 +156,7 @@ protected override void Init() m_AutoRandomSeed = GetProperty0("autoRandomSeed"); m_RandomSeed = GetProperty0("randomSeed"); m_StopAction = GetProperty0("stopAction"); + m_CullingMode = GetProperty0("cullingMode"); m_RingBufferMode = GetProperty0("ringBufferMode"); m_RingBufferLoopRange = GetProperty0("ringBufferLoopRange"); @@ -307,6 +318,7 @@ override public void OnInspectorGUI(InitialModuleUI initial) } GUIPopup(s_Texts.stopAction, m_StopAction, s_Texts.stopActions); + GUIPopup(s_Texts.cullingMode, m_CullingMode, s_Texts.cullingModes); ParticleSystemRingBufferMode ringBufferMode = (ParticleSystemRingBufferMode)GUIPopup(s_Texts.ringBufferMode, m_RingBufferMode, s_Texts.ringBufferModes); if (!m_RingBufferMode.hasMultipleDifferentValues && ringBufferMode == ParticleSystemRingBufferMode.LoopUntilReplaced) diff --git a/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/RendererModuleUI.cs b/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/RendererModuleUI.cs index ff4c3926cd..35f83e7f67 100644 --- a/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/RendererModuleUI.cs +++ b/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/RendererModuleUI.cs @@ -274,12 +274,15 @@ override public void OnInspectorGUI(InitialModuleUI initial) { if (renderMode != RenderMode.Mesh) GUIFloat(s_Texts.normalDirection, m_NormalDirection); - - if (m_Material != null) // The renderer's material list could be empty - GUIObject(s_Texts.material, m_Material); } } + if (renderMode != RenderMode.None) + { + if (m_Material != null) // The renderer's material list could be empty + GUIObject(s_Texts.material, m_Material); + } + if (m_TrailMaterial != null) // The renderer's material list could be empty GUIObject(s_Texts.trailMaterial, m_TrailMaterial); diff --git a/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/TrailModuleUI.cs b/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/TrailModuleUI.cs index d0db6cc3c1..bdb29270c0 100644 --- a/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/TrailModuleUI.cs +++ b/Editor/Mono/ParticleSystemEditor/ParticleSystemModules/TrailModuleUI.cs @@ -28,6 +28,7 @@ class Texts public GUIContent shadowBias = EditorGUIUtility.TrTextContent("Shadow Bias", "Apply a shadow bias to prevent self-shadowing artifacts. The specified value is the proportion of the trail width at each segment."); public GUIContent ribbonCount = EditorGUIUtility.TrTextContent("Ribbon Count", "Select how many ribbons to render throughout the Particle System."); public GUIContent splitSubEmitterRibbons = EditorGUIUtility.TrTextContent("Split Sub Emitter Ribbons", "When used on a sub emitter, ribbons will connect particles from each parent particle independently."); + public GUIContent attachRibbonsToTransform = EditorGUIUtility.TrTextContent("Attach Ribbons to Transform", "Connect each ribbon to the position of the Transform Component."); public GUIContent[] trailModeOptions = { @@ -62,6 +63,7 @@ class Texts SerializedProperty m_ShadowBias; SerializedProperty m_RibbonCount; SerializedProperty m_SplitSubEmitterRibbons; + SerializedProperty m_AttachRibbonsToTransform; public TrailModuleUI(ParticleSystemUI owner, SerializedObject o, string displayName) : base(owner, o, "TrailModule", displayName) @@ -94,6 +96,7 @@ protected override void Init() m_ShadowBias = GetProperty("shadowBias"); m_RibbonCount = GetProperty("ribbonCount"); m_SplitSubEmitterRibbons = GetProperty("splitSubEmitterRibbons"); + m_AttachRibbonsToTransform = GetProperty("attachRibbonsToTransform"); } override public void OnInspectorGUI(InitialModuleUI initial) @@ -113,6 +116,7 @@ override public void OnInspectorGUI(InitialModuleUI initial) { GUIInt(s_Texts.ribbonCount, m_RibbonCount); GUIToggle(s_Texts.splitSubEmitterRibbons, m_SplitSubEmitterRibbons); + GUIToggle(s_Texts.attachRibbonsToTransform, m_AttachRibbonsToTransform); } } diff --git a/Editor/Mono/ParticleSystemEditor/ParticleSystemUI.cs b/Editor/Mono/ParticleSystemEditor/ParticleSystemUI.cs index 8fa5a25abb..6311362d91 100644 --- a/Editor/Mono/ParticleSystemEditor/ParticleSystemUI.cs +++ b/Editor/Mono/ParticleSystemEditor/ParticleSystemUI.cs @@ -427,7 +427,7 @@ void UpdateParticleSystemInfoString() if (supportsCullingText != m_SupportsCullingText || m_SupportsCullingTextLabel == null) { m_SupportsCullingText = supportsCullingText; - m_SupportsCullingTextLabel = "Automatic culling is disabled because: " + supportsCullingText.Replace("\n", "\n" + s_Texts.bulletPoint); + m_SupportsCullingTextLabel = "Procedural simulation is not supported because: " + supportsCullingText.Replace("\n", "\n" + s_Texts.bulletPoint); } } else diff --git a/Editor/Mono/PerformanceTools/FrameDebugger.cs b/Editor/Mono/PerformanceTools/FrameDebugger.cs index e7501eb423..2cca28353c 100644 --- a/Editor/Mono/PerformanceTools/FrameDebugger.cs +++ b/Editor/Mono/PerformanceTools/FrameDebugger.cs @@ -127,7 +127,7 @@ internal struct FrameDebuggerEventData public int subShaderIndex; public int shaderPassIndex; public string shaderKeywords; - public int rendererInstanceID; + public int componentInstanceID; public Mesh mesh; public int meshInstanceID; public int meshSubset; diff --git a/Editor/Mono/PlayerSettings.bindings.cs b/Editor/Mono/PlayerSettings.bindings.cs index 9f9d15dda8..fb1edb4611 100644 --- a/Editor/Mono/PlayerSettings.bindings.cs +++ b/Editor/Mono/PlayerSettings.bindings.cs @@ -1098,5 +1098,11 @@ public static extern bool enableMetalAPIValidation [StaticAccessor("GetPlayerSettings().GetEditorOnly()")] internal static extern bool RelaunchProjectIfScriptRuntimeVersionHasChanged(); + + [StaticAccessor("GetPlayerSettings()")] + public static extern bool GetWsaHolographicRemotingEnabled(); + + [StaticAccessor("GetPlayerSettings()")] + public static extern void SetWsaHolographicRemotingEnabled(bool enabled); } } diff --git a/Editor/Mono/PlayerSettingsAndroid.bindings.cs b/Editor/Mono/PlayerSettingsAndroid.bindings.cs index dc0c0acbec..5a49e97515 100644 --- a/Editor/Mono/PlayerSettingsAndroid.bindings.cs +++ b/Editor/Mono/PlayerSettingsAndroid.bindings.cs @@ -67,6 +67,9 @@ public enum AndroidSdkVersions // Android 8.1, "Oreo", API level 27 AndroidApiLevel27 = 27, + + // Android 9.0, "Pie", API level 28 + AndroidApiLevel28 = 28, } // Preferred application install location @@ -397,11 +400,11 @@ public static extern bool startInFullscreen set; } - public static extern int jvmMaxHeapSize + public static extern bool renderOutsideSafeArea { - [NativeMethod("GetAndroidJvmMaxHeapSize")] + [NativeMethod("GetAndroidRenderOutsideSafeArea")] get; - [NativeMethod("SetAndroidJvmMaxHeapSize")] + [NativeMethod("SetAndroidRenderOutsideSafeArea")] set; } } diff --git a/Editor/Mono/PlayerSettingsSwitch.bindings.cs b/Editor/Mono/PlayerSettingsSwitch.bindings.cs index f435d72098..076266ecd8 100644 --- a/Editor/Mono/PlayerSettingsSwitch.bindings.cs +++ b/Editor/Mono/PlayerSettingsSwitch.bindings.cs @@ -49,7 +49,8 @@ public enum Languages Korean, } - public enum StartupUserAccount + public enum + StartupUserAccount { None = 0, Required = 1, @@ -367,6 +368,9 @@ extern public static string[] localCommunicationIds [NativeProperty("switchDataLossConfirmation", TargetType.Field)] extern public static bool isDataLossConfirmationEnabled { get; set; } + [NativeProperty("switchUserAccountLockEnabled", TargetType.Field)] + extern public static bool isUserAccountLockEnabled { get; set; } + [Obsolete("isDataLossConfirmation was renamed to isDataLossConfirmationEnabled")] [NativeProperty("switchDataLossConfirmation", TargetType.Field)] extern public static bool isDataLossConfirmation { get; set; } diff --git a/Editor/Mono/Prefabs/PrefabImporterEditor.cs b/Editor/Mono/Prefabs/PrefabImporterEditor.cs index b5cc37005b..d95d7175aa 100644 --- a/Editor/Mono/Prefabs/PrefabImporterEditor.cs +++ b/Editor/Mono/Prefabs/PrefabImporterEditor.cs @@ -15,6 +15,8 @@ internal class PrefabImporterEditor : AssetImporterEditor { static GUIContent s_OpenContent = EditorGUIUtility.TrTextContent("Open Prefab"); static GUIContent s_BaseContent = EditorGUIUtility.TrTextContent("Base"); + static string s_LocalizedTitleMultiplePrefabs = L10n.Tr("Prefab Assets"); + static string s_LocalizedTitleSinglePrefab = L10n.Tr("Prefab Asset"); int m_HasMixedBaseVariants = -1; @@ -41,6 +43,17 @@ void CacheHasMixedBaseVariants() public override bool showImportedObject { get { return false; } } + internal override string targetTitle + { + get + { + if (assetTargets == null || assetTargets.Length == 1 || !m_AllowMultiObjectAccess) + return assetTarget != null ? assetTarget.name : s_LocalizedTitleSinglePrefab; + else + return assetTargets.Length + " " + s_LocalizedTitleMultiplePrefabs; + } + } + internal override void OnHeaderControlsGUI() { var variantBase = PrefabUtility.GetCorrespondingObjectFromSource(assetTarget); diff --git a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs index e14e7d536a..1d23d08821 100644 --- a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs +++ b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs @@ -17,19 +17,28 @@ internal class PrefabOverridesWindow : PopupWindowContent const float k_HeaderHeight = 32f; const float k_ButtonWidth = 120; const float k_HeaderLeftMargin = 6; + const float k_NoOverridesLabelHeight = 26f; + const float k_ApplyButtonHeight = 32f; + const float k_HelpBoxHeight = 40f; + GameObject[] m_SelectedGameObjects = null; + + // TreeView not used when there are multiple Prefabs. TreeViewState m_TreeViewState; PrefabOverridesTreeView m_TreeView; - GameObject m_SelectedGameObject; GUIContent m_StageContent = new GUIContent(); GUIContent m_InstanceContent = new GUIContent(); GUIContent m_RevertAllContent = new GUIContent(); GUIContent m_ApplyAllContent = new GUIContent(); - bool m_Immutable; + bool m_AnyOverrides; + bool m_Disconnected; bool m_InvalidComponentOnInstance; + bool m_ModelPrefab; + bool m_Immutable; bool m_InvalidComponentOnAsset; + static class Styles { public static GUIContent revertAllContent = EditorGUIUtility.TrTextContent("Revert All", "Revert all overrides."); @@ -49,46 +58,76 @@ static Styles() internal PrefabOverridesWindow(GameObject selectedGameObject) { - m_SelectedGameObject = selectedGameObject; + m_SelectedGameObjects = new GameObject[] { selectedGameObject }; m_TreeViewState = new TreeViewState(); m_TreeView = new PrefabOverridesTreeView(selectedGameObject, m_TreeViewState); - GameObject prefabAssetRoot = PrefabUtility.GetCorrespondingObjectFromSource(m_SelectedGameObject); + GameObject prefabAssetRoot = PrefabUtility.GetCorrespondingObjectFromSource(selectedGameObject); - m_TreeView.SetApplyTarget(m_SelectedGameObject, prefabAssetRoot, AssetDatabase.GetAssetPath(prefabAssetRoot)); + m_TreeView.SetApplyTarget(selectedGameObject, prefabAssetRoot, AssetDatabase.GetAssetPath(prefabAssetRoot)); - UpdateText(prefabAssetRoot); + UpdateTextSingle(prefabAssetRoot); + UpdateStatusChecks(selectedGameObject); + } - m_Immutable = PrefabUtility.IsPartOfImmutablePrefab(prefabAssetRoot); - m_InvalidComponentOnInstance = PrefabUtility.HasInvalidComponent(m_SelectedGameObject); - m_InvalidComponentOnAsset = PrefabUtility.HasInvalidComponent(prefabAssetRoot); + internal PrefabOverridesWindow(GameObject[] selectedGameObjects) + { + m_SelectedGameObjects = selectedGameObjects; + UpdateTextMultiple(); + for (int i = 0; i < m_SelectedGameObjects.Length; i++) + UpdateStatusChecks(m_SelectedGameObjects[i]); } - bool IsDisconnected() + void UpdateStatusChecks(GameObject prefabInstanceRoot) { - return PrefabUtility.IsDisconnectedFromPrefabAsset(m_SelectedGameObject); + if (PrefabUtility.HasPrefabInstanceAnyOverrides(prefabInstanceRoot, false)) + m_AnyOverrides = true; + if (PrefabUtility.IsDisconnectedFromPrefabAsset(prefabInstanceRoot)) + m_Disconnected = true; + if (PrefabUtility.HasInvalidComponent(prefabInstanceRoot)) + m_InvalidComponentOnInstance = true; + + GameObject prefabAssetRoot = PrefabUtility.GetCorrespondingObjectFromSource(prefabInstanceRoot); + + if (PrefabUtility.IsPartOfModelPrefab(prefabAssetRoot)) + m_ModelPrefab = true; + if (PrefabUtility.IsPartOfImmutablePrefab(prefabAssetRoot)) + m_Immutable = true; + if (PrefabUtility.HasInvalidComponent(prefabAssetRoot)) + m_InvalidComponentOnAsset = true; } bool IsShowingActionButton() { - if (m_TreeView.hasModifications || IsDisconnected()) - return true; + return m_AnyOverrides || m_Disconnected; + } - return false; + bool HasMultiSelection() + { + return m_SelectedGameObjects.Length > 1; + } + + bool DisplayingTreeView() + { + return m_AnyOverrides && !HasMultiSelection(); } public override Vector2 GetWindowSize() { var height = k_HeaderHeight; - if (!IsDisconnected()) - height += k_TreeViewPadding.top + m_TreeView.totalHeight + k_TreeViewPadding.bottom; + if (!IsShowingActionButton()) + { + height += k_NoOverridesLabelHeight; + } + else + { + if (DisplayingTreeView()) + height += k_TreeViewPadding.top + m_TreeView.totalHeight + k_TreeViewPadding.bottom; - const float applyButtonHeight = 32f; - if (IsShowingActionButton()) - height += applyButtonHeight; - if (m_TreeView.hasModifications || IsDisconnected()) - height += 40; + if (IsShowingActionButton()) + height += k_ApplyButtonHeight + k_HelpBoxHeight; + } // Width should be no smaller than minimum width, but we could potentially improve // width handling by making it expand if needed based on tree view content. @@ -99,6 +138,13 @@ public override Vector2 GetWindowSize() public override void OnGUI(Rect rect) { + // Escape closes the window + if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape) + { + editorWindow.Close(); + GUIUtility.ExitGUI(); + } + Rect headerRect = GUILayoutUtility.GetRect(20, 10000, k_HeaderHeight, k_HeaderHeight); EditorGUI.DrawRect(headerRect, headerBgColor); @@ -120,20 +166,36 @@ public override void OnGUI(Rect rect) GUILayout.Space(k_TreeViewPadding.top); - if (!IsDisconnected()) + // If we know there are no overrides and thus no meaningful actions we just show that and nothing more. + if (!IsShowingActionButton()) { - Rect treeViewRect = GUILayoutUtility.GetRect(100, 1000, 0, 1000); - m_TreeView.OnGUI(treeViewRect); + EditorGUILayout.LabelField("No Overrides"); + return; } - if (IsShowingActionButton()) + // Display tree view and/or instructions related to it. + if (HasMultiSelection()) { - if (IsDisconnected()) + if (m_InvalidComponentOnAsset || m_InvalidComponentOnInstance || m_ModelPrefab || m_Immutable) + EditorGUILayout.HelpBox( + "Multiple Prefabs selected. Cannot show overrides.\nApplying is not possible for one or more Prefabs. Select individual Prefabs for details.", + MessageType.Info); + else + EditorGUILayout.HelpBox( + "Multiple Prefabs selected. Cannot show overrides.", + MessageType.Info); + } + else + { + if (m_Disconnected) { EditorGUILayout.HelpBox("Disconnected. Cannot show overrides.", MessageType.Warning); } - else if (m_TreeView.hasModifications) + else if (m_AnyOverrides) { + Rect treeViewRect = GUILayoutUtility.GetRect(100, 1000, 0, 1000); + m_TreeView.OnGUI(treeViewRect); + if (m_InvalidComponentOnAsset) EditorGUILayout.HelpBox( "Click on individual items to review and revert.\nThe Prefab file contains an invalid script. Applying is not possible. Enter Prefab Mode and remove the script.", @@ -142,7 +204,7 @@ public override void OnGUI(Rect rect) EditorGUILayout.HelpBox( "Click on individual items to review and revert.\nThe Prefab instance contains an invalid script. Applying is not possible. Remove the script.", MessageType.Info); - else if (PrefabUtility.IsPartOfModelPrefab(m_SelectedGameObject)) + else if (m_ModelPrefab) EditorGUILayout.HelpBox( "Click on individual items to review and revert.\nApplying to a model Prefab is not possible.", MessageType.Info); @@ -154,54 +216,93 @@ public override void OnGUI(Rect rect) EditorGUILayout.HelpBox("Click on individual items to review, revert and apply.", MessageType.Info); } + } - GUILayout.BeginHorizontal(); - - GUILayout.FlexibleSpace(); - - using (new EditorGUI.DisabledScope(m_InvalidComponentOnAsset)) + // Display action buttons (Revert All and Apply All) + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + using (new EditorGUI.DisabledScope(m_InvalidComponentOnAsset)) + { + if (GUILayout.Button(m_RevertAllContent, GUILayout.Width(k_ButtonWidth))) { - if (GUILayout.Button(m_RevertAllContent, GUILayout.Width(k_ButtonWidth))) + if (RevertAll() && editorWindow != null) { - PrefabUtility.RevertPrefabInstance(m_SelectedGameObject, InteractionMode.UserAction); - - if (editorWindow != null) - { - editorWindow.Close(); - GUIUtility.ExitGUI(); - } + editorWindow.Close(); + GUIUtility.ExitGUI(); } + } - using (new EditorGUI.DisabledScope(m_Immutable || m_InvalidComponentOnInstance)) + using (new EditorGUI.DisabledScope(m_Immutable || m_InvalidComponentOnInstance)) + { + if (GUILayout.Button(m_ApplyAllContent, GUILayout.Width(k_ButtonWidth))) { - if (GUILayout.Button(m_ApplyAllContent, GUILayout.Width(k_ButtonWidth))) + if (ApplyAll() && editorWindow != null) { - string assetPath = - PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(m_SelectedGameObject); - if (PrefabUtility.PromptAndCheckoutPrefabIfNeeded(assetPath, PrefabUtility.SaveVerb.Apply)) - { - PrefabUtility.ApplyPrefabInstance(m_SelectedGameObject, InteractionMode.UserAction); - - if (editorWindow != null) - { - editorWindow.Close(); - GUIUtility.ExitGUI(); - } - } + editorWindow.Close(); + GUIUtility.ExitGUI(); } } } } + GUILayout.EndHorizontal(); + } - // Escape closes the window - if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape) + bool ApplyAll() + { + // Collect Prefab Asset paths and also check if there's more than one of the same. + HashSet prefabAssetPaths = new HashSet(); + bool multipleOfSame = false; + for (int i = 0; i < m_SelectedGameObjects.Length; i++) { - editorWindow.Close(); - GUIUtility.ExitGUI(); + string prefabAssetPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(m_SelectedGameObjects[i]); + if (prefabAssetPaths.Contains(prefabAssetPath)) + multipleOfSame = true; + else + prefabAssetPaths.Add(prefabAssetPath); } + + // If more than one instance of the same Prefab Asset, show dialog to user. + if (multipleOfSame && !EditorUtility.DisplayDialog( + "Multiple instances of same Prefab Asset", + "Multiple instances of the same Prefab Asset were detected. Potentially conflicting overrides will be applied sequentially and will overwrite each other.", + "OK", + "Cancel")) + return false; + + // Make sure assets are checked out in version control. + if (!PrefabUtility.PromptAndCheckoutPrefabIfNeeded(prefabAssetPaths.ToArray(), PrefabUtility.SaveVerb.Apply)) + return false; + + // Apply sequentially. + for (int i = 0; i < m_SelectedGameObjects.Length; i++) + PrefabUtility.ApplyPrefabInstance(m_SelectedGameObjects[i], InteractionMode.UserAction); + + return true; + } + + bool RevertAll() + { + for (int i = 0; i < m_SelectedGameObjects.Length; i++) + PrefabUtility.RevertPrefabInstance(m_SelectedGameObjects[i], InteractionMode.UserAction); + + return true; + } + + void UpdateTextSingle(GameObject prefabAsset) + { + Texture2D icon = (Texture2D)AssetDatabase.GetCachedIcon(AssetDatabase.GetAssetPath(prefabAsset)); + string name = prefabAsset.name; + UpdateText(icon, name); + } + + void UpdateTextMultiple() + { + Texture icon = EditorGUIUtility.IconContent("Prefab Icon").image; + string name = "(Multiple Prefabs)"; + UpdateText(icon, name); } - void UpdateText(GameObject prefabAsset) + void UpdateText(Texture assetIcon, string assetName) { var stage = SceneManagement.StageNavigationManager.instance.currentItem; if (stage.isMainStage) @@ -215,8 +316,8 @@ void UpdateText(GameObject prefabAsset) m_StageContent.text = stage.displayName; } - m_InstanceContent.image = (Texture2D)AssetDatabase.GetCachedIcon(AssetDatabase.GetAssetPath(prefabAsset)); - m_InstanceContent.text = prefabAsset.name; + m_InstanceContent.image = assetIcon; + m_InstanceContent.text = assetName; m_RevertAllContent.text = Styles.revertAllContent.text; m_RevertAllContent.tooltip = Styles.revertAllContent.tooltip; @@ -226,7 +327,7 @@ void UpdateText(GameObject prefabAsset) applyAllContent = Styles.applyAllToBaseContent; m_ApplyAllContent.text = applyAllContent.text; - m_ApplyAllContent.tooltip = string.Format(applyAllContent.tooltip, prefabAsset.name); + m_ApplyAllContent.tooltip = string.Format(applyAllContent.tooltip, assetName); } } } diff --git a/Editor/Mono/Prefabs/PrefabUtility.bindings.cs b/Editor/Mono/Prefabs/PrefabUtility.bindings.cs index 7953e088d2..728dd0a803 100644 --- a/Editor/Mono/Prefabs/PrefabUtility.bindings.cs +++ b/Editor/Mono/Prefabs/PrefabUtility.bindings.cs @@ -30,6 +30,7 @@ public sealed partial class PrefabUtility extern private static Object GetCorrespondingObjectFromSourceAtPath_internal([NotNull] Object obj, string prefabAssetPath); // Retrieves the prefab object representation. + [Obsolete("Use GetPrefabInstanceHandle for Prefab instances. Handles for Prefab Assets has been discontinued.")] [StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)] extern public static Object GetPrefabObject(Object targetObject); @@ -56,6 +57,9 @@ public sealed partial class PrefabUtility [StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)] extern public static void SetPropertyModifications(Object targetPrefab, PropertyModification[] modifications); + [FreeFunction] + extern public static bool HasPrefabInstanceAnyOverrides(GameObject instanceRoot, bool includeDefaultOverrides); + // Instantiate an asset that is referenced by a prefab and use it on the prefab instance. [FreeFunction] [NativeHeader("Editor/Src/Prefabs/AttachedPrefabAsset.h")] @@ -72,12 +76,13 @@ public sealed partial class PrefabUtility extern public static void MergeAllPrefabInstances(Object targetObject); // Disconnects the prefab instance from its parent prefab. + [Obsolete("The concept of disconnecting Prefab instances has been deprecated.")] [FreeFunction] extern public static void DisconnectPrefabInstance(Object targetObject); [StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)] [NativeThrows] - extern public static GameObject[] UnpackPrefabInstanceAndReturnNewOutermostRoots(GameObject root, PrefabUnpackMode unpackMode); + extern public static GameObject[] UnpackPrefabInstanceAndReturnNewOutermostRoots(GameObject instanceRoot, PrefabUnpackMode unpackMode); [StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)] [NativeThrows] @@ -88,6 +93,7 @@ public sealed partial class PrefabUtility extern public static void LoadPrefabContentsIntoPreviewScene(string prefabPath, Scene scene); // Connect the source prefab to the game object, which replaces the instance content with the content of the prefab + [Obsolete("Use RevertPrefabInstance. Prefabs instances can no longer be connected to Prefab Assets they are not an instance of to begin with.")] [StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)] [NativeThrows] extern public static GameObject ConnectGameObjectToPrefab([NotNull] GameObject go, [NotNull] GameObject sourcePrefab); @@ -105,26 +111,35 @@ public sealed partial class PrefabUtility // Connects the game object to the prefab that it was last connected to. [FreeFunction] + [Obsolete("Use RevertPrefabInstance.")] extern public static bool ReconnectToLastPrefab(GameObject go); // Resets the properties of the component or game object to the parent prefab state + [Obsolete("Use RevertObjectOverride.")] [StaticAccessor("PrefabUtilityBindings", StaticAccessorType.DoubleColon)] extern public static bool ResetToPrefabState(Object obj); + // Resets the properties of the component or game object to the parent prefab state + [NativeMethod("PrefabUtilityBindings::ResetToPrefabState", IsFreeFunction = true)] + extern private static bool RevertObjectOverride_Internal(Object obj); + [FreeFunction] extern public static bool IsAddedComponentOverride([NotNull] Object component); // Resets the properties of all objects in the prefab, including child game objects and components that were added to the prefab instance + [Obsolete("Use the overload that takes an InteractionMode parameter.")] [FreeFunction] extern public static bool RevertPrefabInstance([NotNull] GameObject go); + // Resets the properties of all objects in the prefab, including child game objects and components that were added to the prefab instance + [NativeMethod("RevertPrefabInstance", IsFreeFunction = true)] + extern private static bool RevertPrefabInstance_Internal([NotNull] GameObject go); + // Helper function to find the prefab root of an object [FreeFunction] [Obsolete("Use GetOutermostPrefabInstanceRoot if source is a Prefab instance or source.transform.root.gameObject if source is a Prefab Asset object.")] extern public static GameObject FindPrefabRoot([NotNull] GameObject source); -#pragma warning disable 0618 // Type or member is obsolete - internal static GameObject CreateVariant(GameObject assetRoot, string path) { if (assetRoot == null) @@ -146,11 +161,11 @@ internal static GameObject CreateVariant(GameObject assetRoot, string path) if (!Paths.IsValidAssetPath(path, ".prefab")) throw new ArgumentException("Given path is not valid: '" + path + "'"); - return CreateVariant_Internal(assetRoot, path, ReplacePrefabOptions.Default); + return CreateVariant_Internal(assetRoot, path); } [NativeMethod("CreateVariant", IsFreeFunction = true)] - extern private static GameObject CreateVariant_Internal([NotNull] GameObject original, string path, ReplacePrefabOptions replaceOptions); + extern private static GameObject CreateVariant_Internal([NotNull] GameObject original, string path); private enum PrefabCreationFlags { @@ -158,6 +173,8 @@ private enum PrefabCreationFlags CreateVariant = 1, } + // TODO: Having an non-obsolete method that takes an obsolete enum types as parameter is no good. +#pragma warning disable 0618 // Type or member is obsolete private static GameObject SavePrefab(GameObject inputObject, string path, ReplacePrefabOptions replaceOptions, PrefabCreationFlags creationFlags) { if (inputObject == null) @@ -175,13 +192,17 @@ private static GameObject SavePrefab(GameObject inputObject, string path, Replac throw new ArgumentException("Given path does not exist: '" + path + "'"); string prefabGUID = AssetDatabase.AssetPathToGUID(path); - if (!VerifyNestingFromScript(new GameObject[] {inputObject}, prefabGUID, PrefabUtility.GetPrefabObject(inputObject))) + if (!VerifyNestingFromScript(new GameObject[] {inputObject}, prefabGUID, PrefabUtility.GetPrefabInstanceHandle(inputObject))) throw new ArgumentException("Cyclic nesting detected"); return SavePrefab_Internal(inputObject, path, replaceOptions, creationFlags); } +#pragma warning restore 0618 // Type or member is obsolete + + // TODO: Having an non-obsolete method that takes an obsolete enum types as parameter is no good. [NativeMethod("SavePrefab", IsFreeFunction = true)] +#pragma warning disable 0618 // Type or member is obsolete extern private static GameObject SavePrefab_Internal([NotNull] GameObject root, string path, ReplacePrefabOptions replaceOptions, PrefabCreationFlags createOptions); #pragma warning restore 0618 // Type or member is obsolete @@ -201,7 +222,7 @@ internal static void AddGameObjectsToPrefabAndConnect(GameObject[] gameObjects, Object targetPrefabInstance = null; - var targetPrefabObject = PrefabUtility.GetPrefabObject(targetPrefab); + var targetPrefabObject = PrefabUtility.GetPrefabAssetHandle(targetPrefab); foreach (GameObject go in gameObjects) { @@ -232,7 +253,7 @@ internal static void AddGameObjectsToPrefabAndConnect(GameObject[] gameObjects, if (PrefabUtility.IsPartOfNonAssetPrefabInstance(go)) { var correspondingGO = PrefabUtility.GetCorrespondingObjectFromSource(go); - var correspondingGOPrefabObject = PrefabUtility.GetPrefabObject(correspondingGO); + var correspondingGOPrefabObject = PrefabUtility.GetPrefabAssetHandle(correspondingGO); if (targetPrefabObject == correspondingGOPrefabObject) throw new ArgumentException("GameObject is already part of target prefab"); } @@ -299,6 +320,11 @@ internal static void AddGameObjectsToPrefabAndConnect(GameObject[] gameObjects, [FreeFunction] extern public static bool IsPartOfVariantPrefab([NotNull] Object componentOrGameObject); + // Returns true if the object is from a Prefab Asset which is not editable, or an instance of such a Prefab + // Examples are Model Prefabs and Prefabs in read-only folders. + [FreeFunction] + extern public static bool IsPartOfImmutablePrefab([NotNull] Object componentOrGameObject); + [FreeFunction] extern public static bool IsDisconnectedFromPrefabAsset([NotNull] Object componentOrGameObject); diff --git a/Editor/Mono/Prefabs/PrefabUtility.cs b/Editor/Mono/Prefabs/PrefabUtility.cs index 8279a9f737..4c262b311d 100644 --- a/Editor/Mono/Prefabs/PrefabUtility.cs +++ b/Editor/Mono/Prefabs/PrefabUtility.cs @@ -296,10 +296,7 @@ public static void RevertPrefabInstance(GameObject instanceRoot, InteractionMode CheckInstanceIsNotPersistent(instanceRoot); - // The concept of disconnecting are being deprecated. For now use FindRootGameObjectWithSameParentPrefab - // to re-connect existing disconnected prefabs. - #pragma warning disable 0618 // Type or member is obsolete - GameObject prefabInstanceRoot = FindRootGameObjectWithSameParentPrefab(instanceRoot); + GameObject prefabInstanceRoot = GetOutermostPrefabInstanceRoot(instanceRoot); var actionName = "Revert Prefab Instance"; HashSet hierarchy = null; @@ -311,20 +308,19 @@ public static void RevertPrefabInstance(GameObject instanceRoot, InteractionMode Undo.RegisterFullObjectHierarchyUndo(prefabInstanceRoot, actionName); } - if (isDisconnected) - { - ReconnectToLastPrefab(prefabInstanceRoot); - - if (action == InteractionMode.UserAction) - Undo.RegisterCreatedObjectUndo(GetPrefabInstanceHandle(prefabInstanceRoot), actionName); - } - - RevertPrefabInstance(prefabInstanceRoot); + RevertPrefabInstance_Internal(prefabInstanceRoot); if (action == InteractionMode.UserAction) { + if (isDisconnected) + { + Undo.RegisterCreatedObjectUndo(GetPrefabInstanceHandle(prefabInstanceRoot), actionName); + } + RegisterNewObjects(prefabInstanceRoot, hierarchy, actionName); } + + EditorUtility.ForceRebuildInspectors(); } public static void ApplyPrefabInstance(GameObject instanceRoot, InteractionMode action) @@ -333,10 +329,7 @@ public static void ApplyPrefabInstance(GameObject instanceRoot, InteractionMode CheckInstanceIsNotPersistent(instanceRoot); - // The concept of disconnecting are being deprecated. For now use FindRootGameObjectWithSameParentPrefab - // to re-connect existing disconnected prefabs. - #pragma warning disable 0618 // Type or member is obsolete - GameObject prefabInstanceRoot = FindRootGameObjectWithSameParentPrefab(instanceRoot); + GameObject prefabInstanceRoot = GetOutermostPrefabInstanceRoot(instanceRoot); var actionName = "Apply instance to prefab"; Object correspondingSourceObject = GetCorrespondingObjectFromSource(prefabInstanceRoot); @@ -391,21 +384,91 @@ private static void MapObjectReferencePropertyToSourceIfApplicable(SerializedPro } public static void ApplyPropertyOverride(SerializedProperty instanceProperty, string assetPath, InteractionMode action) - { - ApplyPropertyOverride(instanceProperty, assetPath, action, true); - } - - static void ApplyPropertyOverride(SerializedProperty instanceProperty, string assetPath, InteractionMode action, bool singlePropertyOnly) { DateTime startTime = DateTime.UtcNow; Object prefabInstanceObject = instanceProperty.serializedObject.targetObject; + CheckInstanceIsNotPersistent(prefabInstanceObject); + + ApplyPropertyOverrides(prefabInstanceObject, instanceProperty, assetPath, action); + + Analytics.SendApplyEvent( + Analytics.ApplyScope.PropertyOverride, + prefabInstanceObject, + assetPath, + action, + startTime, + IsPropertyOverrideDefaultOverrideComparedToAnySource(instanceProperty) + ); + } + + // This method is called both from ApplyPropertyOverride (one time) and ApplyObjectOverride (many times). + // In the former case, optionalSingleInstanceProperty is passed along as is the only property that should be processed. + // In the latter, all properties in the prefabInstanceObject are iterated. + // An alternative approach was considered where the method takes an array of SerializedProperties, + // but since there can be thousands of those in a component, and they would each have to be copied from the iterator, + // it's better to handle properties inline as the iterator iterates over them. + // This does mean that we need to cache the SerializedObjects that end up being touched. + // Those are that of the prefabInstanceObject itself, and the chain of corresponding object + // all the way to the Prefab at the specified assetPath. + // Since calling ApplyModifiedProperties on a SerializedObject can trigger a whole chain of imports, + // we don't want to create and call ApplyModifiedProperties more than once for each SerializedObject, hence the caching. + // We also can't swap the inner and outer loop, iterating the chain of corresponding objects in the outer loop + // and the SerializedProperties in the inner loop, since we only process overridden properties, so it would + // again require storing information about that for each property in some kind of list, which we want to avoid. + static void ApplyPropertyOverrides(Object prefabInstanceObject, SerializedProperty optionalSingleInstanceProperty, string assetPath, InteractionMode action) + { + bool singleProperty = optionalSingleInstanceProperty != null; Object prefabSourceObject = GetCorrespondingObjectFromSourceAtPath(prefabInstanceObject, assetPath); if (prefabSourceObject == null) return; - if (IsPropertyOverrideDefaultOverrideComparedToAnySource(instanceProperty) && IsObjectOnRootInAsset(prefabInstanceObject, assetPath)) + SerializedObject prefabSourceSerializedObject = new SerializedObject(prefabSourceObject); + + // Cache SerializedObjects used. + List serializedObjects = new List(); + + bool isObjectOnRootInAsset = IsObjectOnRootInAsset(prefabInstanceObject, assetPath); + if (singleProperty) + { + if (optionalSingleInstanceProperty.prefabOverride) + ApplySingleProperty(optionalSingleInstanceProperty, prefabSourceSerializedObject, assetPath, isObjectOnRootInAsset, true, serializedObjects, action); + } + else + { + SerializedObject so = new SerializedObject(prefabInstanceObject); + SerializedProperty property = so.GetIterator(); + while (property.Next(property.hasChildren)) + { + if (property.prefabOverride) + ApplySingleProperty(property, prefabSourceSerializedObject, assetPath, isObjectOnRootInAsset, false, serializedObjects, action); + } + } + + // Write modified value to prefab source object. + for (int i = 0; i < serializedObjects.Count; i++) + { + serializedObjects[i].ApplyModifiedProperties(); + if (action == InteractionMode.UserAction) + Undo.FlushUndoRecordObjects(); // flush'es ensure that SavePrefab() on undo/redo on the source happens in the right order + } + } + + // Since method is called for each overridden property in a component. + // That may be thousands of times if a component has lots of array data. + // We provide as much information as possible to the method as parameters + // so we don't have to recalculate it for each call. + static void ApplySingleProperty( + SerializedProperty instanceProperty, + SerializedObject prefabSourceSerializedObject, + string assetPath, + bool isObjectOnRootInAsset, + bool singlePropertyOnly, + List serializedObjects, + InteractionMode action) + { + if (isObjectOnRootInAsset && IsPropertyOverrideDefaultOverrideComparedToAnySource(instanceProperty)) { if (singlePropertyOnly) { @@ -414,12 +477,14 @@ static void ApplyPropertyOverride(SerializedProperty instanceProperty, string as if (action == InteractionMode.AutomatedAction) Debug.LogWarning("Cannot apply default-override property, since it is protected from being applied or reverted."); else - EditorUtility.DisplayDialog("Cannot apply default-override property", "Default-override properties are protected from being applied or reverted.", "OK"); + EditorUtility.DisplayDialog( + "Cannot apply default-override property", + "Default-override properties are protected from being applied or reverted.", + "OK"); } return; } - SerializedObject prefabSourceSerializedObject = new SerializedObject(prefabSourceObject); prefabSourceSerializedObject.CopyFromSerializedProperty(instanceProperty); SerializedProperty sourceProperty = prefabSourceSerializedObject.FindProperty(instanceProperty.propertyPath); @@ -438,47 +503,46 @@ static void ApplyPropertyOverride(SerializedProperty instanceProperty, string as if (action == InteractionMode.AutomatedAction) Debug.LogWarning("Cannot apply reference to scene object that is not part of apply target prefab."); else - EditorUtility.DisplayDialog("Cannot apply reference to object in scene", "A reference to an object in the scene cannot be applied to the Prefab asset.", "OK"); + EditorUtility.DisplayDialog( + "Cannot apply reference to object in scene", + "A reference to an object in the scene cannot be applied to the Prefab asset.", + "OK"); } return; } } - // Write modified value to prefab source object. - prefabSourceSerializedObject.ApplyModifiedProperties(); + // Apply target SerializedObject should get ApplyModifiedProperties called first. + if (serializedObjects.Count == 0) + serializedObjects.Add(prefabSourceSerializedObject); - if (action == InteractionMode.UserAction) - Undo.FlushUndoRecordObjects(); // flush'es ensure that SavePrefab() on undo/redo on the source happens in the right order - - // Clear overrides for property in prefab instance and outer prefabs that are using(nesting) the prefab source. + // Clear overrides for property in Prefab instance and outer Prefabs that are using(nesting) the Prefab source. // Otherwise applied modification would appear to jump back to the value it had before applying. + Object prefabInstanceObject = instanceProperty.serializedObject.targetObject; + Object prefabSourceObject = prefabSourceSerializedObject.targetObject; Object outerPrefabObject = prefabInstanceObject; + int sourceIndex = 1; while (outerPrefabObject != prefabSourceObject) { - SerializedObject outerPrefabSerializedObject = new SerializedObject(outerPrefabObject); + SerializedObject outerPrefabSerializedObject; + if (sourceIndex >= serializedObjects.Count) + { + outerPrefabSerializedObject = new SerializedObject(outerPrefabObject); + serializedObjects.Add(outerPrefabSerializedObject); + } + else + { + outerPrefabSerializedObject = serializedObjects[sourceIndex]; + } + SerializedProperty outerPrefabProp = outerPrefabSerializedObject.FindProperty(instanceProperty.propertyPath); if (outerPrefabProp.prefabOverride) { outerPrefabProp.prefabOverride = false; - outerPrefabSerializedObject.ApplyModifiedProperties(); - - if (action == InteractionMode.UserAction) - Undo.FlushUndoRecordObjects(); } - outerPrefabObject = PrefabUtility.GetCorrespondingObjectFromSource(outerPrefabObject); - } - // If this method is called as part of ApplyObjectOverride, we don't want to send analytics here too. - if (singlePropertyOnly) - { - Analytics.SendApplyEvent( - Analytics.ApplyScope.PropertyOverride, - prefabInstanceObject, - assetPath, - action, - startTime, - IsPropertyOverrideDefaultOverrideComparedToAnySource(instanceProperty) - ); + outerPrefabObject = PrefabUtility.GetCorrespondingObjectFromSource(outerPrefabObject); + sourceIndex++; } } @@ -498,13 +562,7 @@ public static void ApplyObjectOverride(Object instanceComponentOrGameObject, str CheckInstanceIsNotPersistent(instanceComponentOrGameObject); - SerializedObject so = new SerializedObject(instanceComponentOrGameObject); - SerializedProperty property = so.GetIterator(); - while (property.Next(property.hasChildren)) - { - if (property.prefabOverride) - ApplyPropertyOverride(property, assetPath, action, false); - } + ApplyPropertyOverrides(instanceComponentOrGameObject, null, assetPath, action); Analytics.SendApplyEvent( Analytics.ApplyScope.ObjectOverride, @@ -525,7 +583,7 @@ public static void RevertObjectOverride(Object instanceComponentOrGameObject, In if (action == InteractionMode.UserAction) Undo.RegisterCompleteObjectUndo(instanceComponentOrGameObject, "Revert component property overrides"); - PrefabUtility.ResetToPrefabState(instanceComponentOrGameObject); + PrefabUtility.RevertObjectOverride_Internal(instanceComponentOrGameObject); } public static void ApplyAddedComponent(Component component, string assetPath, InteractionMode action) @@ -598,7 +656,7 @@ private static bool IsPrefabInstanceObjectOf(Object instance, Object source) return true; } - if (GetPrefabObject(o) == source) + if (GetPrefabAssetHandle(o) == source) { return true; } @@ -641,7 +699,7 @@ public static void ApplyRemovedComponent(GameObject instanceGameObject, Componen SavePrefabAsset(prefabAsset); } - var prefabInstanceObject = PrefabUtility.GetPrefabObject(instanceGameObject); + var prefabInstanceObject = PrefabUtility.GetPrefabInstanceHandle(instanceGameObject); if (action == InteractionMode.UserAction) Undo.RegisterCompleteObjectUndo(prefabInstanceObject, actionName); @@ -682,7 +740,7 @@ public static void RevertRemovedComponent(GameObject instanceGameObject, Compone CheckInstanceIsNotPersistent(instanceGameObject); var actionName = "Revert Prefab removed component"; - var prefabInstanceObject = PrefabUtility.GetPrefabObject(instanceGameObject); + var prefabInstanceObject = PrefabUtility.GetPrefabInstanceHandle(instanceGameObject); if (action == InteractionMode.UserAction) Undo.RegisterCompleteObjectUndo(instanceGameObject, actionName); @@ -737,24 +795,12 @@ public static void ApplyAddedGameObject(GameObject gameObject, string assetPath, if (action == InteractionMode.UserAction) { var createdAssetObject = GetCorrespondingObjectFromSourceInAsset(gameObject, prefabSourceGameObjectParent); - Undo.RegisterCreatedObjectUndo(createdAssetObject, actionName); - - //var instanceImmediateSourceObject = GetCorrespondingObjectFromSource(prefabInstanceGameObject); - //if (instanceImmediateSourceObject != createdAssetObject) - //{ - // // The asset containing this object is not actually changed and import/merge - // // logic would assure that it is create/deleted correctly based on the changes - // // in the source asset ("createdAssetObject" prefab asset) - // // However, during "redo" the import/merge will happed too late for the - // // following "register created object" operation on the instance object. - // // This would cause the instance to reference a non-existing corresponding - // // object which causes a disconnect. - // Undo.RegisterCreatedObjectUndo(instanceImmediateSourceObject, actionName); - //} - - //Undo.RegisterCreatedObjectUndo(instanceRoot, actionName); + if (createdAssetObject != null) + { + Undo.RegisterCreatedObjectUndo(createdAssetObject, actionName); - EditorUtility.ForceRebuildInspectors(); + EditorUtility.ForceRebuildInspectors(); + } } Analytics.SendApplyEvent( @@ -928,7 +974,7 @@ public static Object GetPrefabParent(Object obj) } // Creates an empty prefab at given path. - // TODO Steen Lund 2017 11 28 This needs to be marked OBSOLETE + [Obsolete("The concept of creating a completely empty Prefab has been discontinued. You can however use SaveAsPrefabAsset with an empty GameObject.")] public static Object CreateEmptyPrefab(string path) { // This is here to simulate previous behaviour @@ -944,8 +990,6 @@ public static Object CreateEmptyPrefab(string path) return PrefabUtility.GetPrefabObject(assetObject); } -#pragma warning disable CS0618 // Type or member is obsolete - public static GameObject SavePrefabAsset(GameObject asset) { if (asset == null) @@ -966,24 +1010,26 @@ public static GameObject SavePrefabAsset(GameObject asset) if (root != asset) throw new ArgumentException("GameObject to save Prefab from must be a Prefab root"); +#pragma warning disable CS0618 // Type or member is obsolete return SavePrefab(root, path, ReplacePrefabOptions.Default, PrefabCreationFlags.None); +#pragma warning restore CS0618 // Type or member is obsolete } - private static void SaveAsPrefabAssetArgumentCheck(GameObject root) + private static void SaveAsPrefabAssetArgumentCheck(GameObject instanceRoot) { - if (root == null) + if (instanceRoot == null) throw new ArgumentNullException("Parameter root is null"); - if (EditorUtility.IsPersistent(root)) + if (EditorUtility.IsPersistent(instanceRoot)) throw new ArgumentException("Can't save persistent object as a Prefab asset"); - if (IsPrefabAssetMissing(root)) + if (IsPrefabAssetMissing(instanceRoot)) throw new ArgumentException("Can't save Prefab instance with missing asset as a Prefab. You may unpack the instance and save the unpacked GameObjects as a Prefab."); - var instanceRoot = GetOutermostPrefabInstanceRoot(root); - if (instanceRoot) + var actualInstanceRoot = GetOutermostPrefabInstanceRoot(instanceRoot); + if (actualInstanceRoot) { - if (instanceRoot != root) + if (actualInstanceRoot != instanceRoot) throw new ArgumentException("Can't save part of a Prefab instance as a Prefab"); } } @@ -994,37 +1040,41 @@ private static bool IsPrefabInstanceRoot(GameObject gameObject) return instanceRoot != null && instanceRoot == gameObject; } - public static GameObject SaveAsPrefabAsset(GameObject root, string assetPath) + public static GameObject SaveAsPrefabAsset(GameObject instanceRoot, string assetPath) { - SaveAsPrefabAssetArgumentCheck(root); + SaveAsPrefabAssetArgumentCheck(instanceRoot); PrefabCreationFlags creationFlags = PrefabCreationFlags.None; - if (IsPrefabInstanceRoot(root)) + if (IsPrefabInstanceRoot(instanceRoot)) creationFlags = PrefabCreationFlags.CreateVariant; - return SavePrefab(root, assetPath, ReplacePrefabOptions.Default, creationFlags); +#pragma warning disable CS0618 // Type or member is obsolete + return SavePrefab(instanceRoot, assetPath, ReplacePrefabOptions.Default, creationFlags); +#pragma warning restore CS0618 // Type or member is obsolete } - public static GameObject SaveAsPrefabAssetAndConnect(GameObject root, string assetPath, InteractionMode action) + public static GameObject SaveAsPrefabAssetAndConnect(GameObject instanceRoot, string assetPath, InteractionMode action) { - SaveAsPrefabAssetArgumentCheck(root); + SaveAsPrefabAssetArgumentCheck(instanceRoot); var actionName = "Connect to Prefab"; if (action == InteractionMode.UserAction) { - Undo.RegisterFullObjectHierarchyUndo(root, actionName); + Undo.RegisterFullObjectHierarchyUndo(instanceRoot, actionName); } PrefabCreationFlags creationFlags = PrefabCreationFlags.None; - if (IsPrefabInstanceRoot(root)) + if (IsPrefabInstanceRoot(instanceRoot)) creationFlags = PrefabCreationFlags.CreateVariant; - var assetRoot = SavePrefab(root, assetPath, ReplacePrefabOptions.ConnectToPrefab, creationFlags); +#pragma warning disable CS0618 // Type or member is obsolete + var assetRoot = SavePrefab(instanceRoot, assetPath, ReplacePrefabOptions.ConnectToPrefab, creationFlags); +#pragma warning restore CS0618 // Type or member is obsolete if (action == InteractionMode.UserAction) { - Undo.RegisterCreatedObjectUndo(GetPrefabInstanceHandle(root), actionName); + Undo.RegisterCreatedObjectUndo(GetPrefabInstanceHandle(instanceRoot), actionName); } return assetRoot; @@ -1049,7 +1099,7 @@ internal static void ApplyPrefabInstance(GameObject instance) { // The concept of disconnecting are being deprecated. For now use FindRootGameObjectWithSameParentPrefab // to re-connect existing disconnected prefabs. - var validRoot = PrefabUtility.FindValidUploadPrefabInstanceRoot(instance); + var validRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(instance); var ok = validRoot == instance; if (!ok && PrefabUtility.GetCorrespondingObjectFromOriginalSource(instance) != PrefabUtility.GetCorrespondingObjectFromSource(instance)) throw new ArgumentException("Can't save Prefab from an object that originates from a nested Prefab"); @@ -1063,11 +1113,17 @@ internal static void ApplyPrefabInstance(GameObject instance) var assetObject = GetCorrespondingObjectFromSource(instance); string path = AssetDatabase.GetAssetPath(assetObject); + +#pragma warning disable CS0618 // Type or member is obsolete SavePrefab(instance, path, ReplacePrefabOptions.ConnectToPrefab, PrefabCreationFlags.None); +#pragma warning restore CS0618 // Type or member is obsolete } + // TOOO: Remove entirely once regular methods handle merging + // based on both ids and names on a smarter and more granular level. internal static GameObject ReplacePrefabAssetNameBased(GameObject root, string targetPrefab, bool connectToInstance) { +#pragma warning disable CS0618 // Type or member is obsolete var options = ReplacePrefabOptions.ReplaceNameBased; if (connectToInstance) options |= ReplacePrefabOptions.ConnectToPrefab; @@ -1076,8 +1132,7 @@ internal static GameObject ReplacePrefabAssetNameBased(GameObject root, string t if (IsPartOfNonAssetPrefabInstance(root)) { - var instanceRoot = PrefabUtility.GetPrefabInstanceRootGameObject(root); - if (root != instanceRoot) + if (!IsOutermostPrefabInstanceRoot(root)) throw new ArgumentException("Can't replace with part of Prefab instance. Please specify instance root object or a non-instance object."); createOptions = PrefabCreationFlags.CreateVariant; @@ -1087,22 +1142,26 @@ internal static GameObject ReplacePrefabAssetNameBased(GameObject root, string t throw new ArgumentException("Argument connectToInstance is true but root object is an asset not an instance"); return SavePrefab(root, targetPrefab, options, createOptions); - } - #pragma warning restore CS0618 // Type or member is obsolete + } - //[Obsolete("CreatePrefab() has been deprecated. Use SavePrefab() instead (UnityUpgradable) -> SavePrefab(go, path, ReplacePrefabOptions.Default)")] + // Can't use UnityUpgradable since it doesn't currently support swapping parameter order. + [Obsolete("Use SaveAsPrefabAsset instead.")] public static GameObject CreatePrefab(string path, GameObject go) { return SaveAsPrefabAsset(go, path); } - //[Obsolete("CreatePrefab() has been deprecated. Use SavePrefab() instead (UnityUpgradable) -> SavePrefab(go, path, options)")] -#pragma warning disable CS0618 // Type or member is obsolete + [Obsolete("Use SaveAsPrefabAsset or SaveAsPrefabAssetAndConnect instead.")] public static GameObject CreatePrefab(string path, GameObject go, ReplacePrefabOptions options) { if (options == ReplacePrefabOptions.ConnectToPrefab) return SaveAsPrefabAssetAndConnect(go, path, InteractionMode.AutomatedAction); + else if ((options & ReplacePrefabOptions.ReplaceNameBased) != 0) + { + bool connectToPrefab = (options & ReplacePrefabOptions.ConnectToPrefab) != 0; + return ReplacePrefabAssetNameBased(go, path, connectToPrefab); + } else return SaveAsPrefabAsset(go, path); } @@ -1119,11 +1178,13 @@ public static Object InstantiatePrefab(Object assetComponentOrGameObject, Scene return InstantiatePrefab_internal(assetComponentOrGameObject, destinationScene); } + [Obsolete("Use SaveAsPrefabAsset with a path instead.")] public static GameObject ReplacePrefab(GameObject go, Object targetPrefab) { return ReplacePrefab(go, targetPrefab, ReplacePrefabOptions.Default); } + [Obsolete("Use SaveAsPrefabAsset or SaveAsPrefabAssetAndConnect with a path instead.")] public static GameObject ReplacePrefab(GameObject go, Object targetPrefab, ReplacePrefabOptions replaceOptions) { var targetPrefabObject = PrefabUtility.GetPrefabObject(targetPrefab); @@ -1163,8 +1224,6 @@ public static GameObject ReplacePrefab(GameObject go, Object targetPrefab, Repla return SavePrefab(go, assetPath, replaceOptions, PrefabCreationFlags.None); } -#pragma warning restore CS0618 // Type or member is obsolete - // Returns the corresponding object from its immediate source, or null if it can't be found. public static TObject GetCorrespondingObjectFromSource(TObject componentOrGameObject) where TObject : Object { @@ -1211,7 +1270,6 @@ private static Object GetCorrespondingObjectFromOriginalSource_Internal(Object i } // Given an object, returns its prefab type (None, if it's not a prefab) -#pragma warning disable CS0618 // Type or member is obsolete [Obsolete("Use GetPrefabAssetType and GetPrefabInstanceStatus to get the full picture about Prefab types.")] public static PrefabType GetPrefabType(Object target) { @@ -1251,8 +1309,6 @@ public static PrefabType GetPrefabType(Object target) return PrefabType.PrefabInstance; } -#pragma warning restore CS0618 // Type or member is obsolete - // Called after prefab instances in the scene have been updated public delegate void PrefabInstanceUpdated(GameObject instance); public static PrefabInstanceUpdated prefabInstanceUpdated; @@ -1301,44 +1357,55 @@ internal enum SaveVerb Save, Apply } + internal static bool PromptAndCheckoutPrefabIfNeeded(string assetPath, SaveVerb saveVerb) { + return PromptAndCheckoutPrefabIfNeeded(new string[] { assetPath }, saveVerb); + } + + internal static bool PromptAndCheckoutPrefabIfNeeded(string[] assetPaths, SaveVerb saveVerb) + { + string prefabNoun = assetPaths.Length > 1 ? "Prefabs" : "Prefab"; bool result = Provider.PromptAndCheckoutIfNeeded( - new string[] { assetPath }, + assetPaths, String.Format( - "The version control requires you to check out the Prefab before {0} changes.", - saveVerb == SaveVerb.Save ? "saving" : "applying") + "The version control requires you to check out the {1} before {0} changes.", + saveVerb == SaveVerb.Save ? "saving" : "applying", + prefabNoun + ) ); if (!result) EditorUtility.DisplayDialog( String.Format( - "Could not {0} Prefab", - saveVerb == SaveVerb.Save ? "save" : "apply to"), + "Could not {0} {1}", + saveVerb == SaveVerb.Save ? "save" : "apply to", + prefabNoun), String.Format( - "It was not possible to check out the Prefab so the {0} operation has been canceled.", - saveVerb == SaveVerb.Save ? "save" : "apply"), + "It was not possible to check out the {1} so the {0} operation has been canceled.", + saveVerb == SaveVerb.Save ? "save" : "apply", + prefabNoun), "OK"); return result; } - public static void UnpackPrefabInstance(GameObject root, PrefabUnpackMode unpackMode, InteractionMode action) + public static void UnpackPrefabInstance(GameObject instanceRoot, PrefabUnpackMode unpackMode, InteractionMode action) { - if (!IsPartOfNonAssetPrefabInstance(root)) + if (!IsPartOfNonAssetPrefabInstance(instanceRoot)) throw new ArgumentException("UnpackPrefabInstance must be called with a Prefab instance."); - if (GetPrefabInstanceRootGameObject(root) != root) + if (!IsOutermostPrefabInstanceRoot(instanceRoot)) throw new ArgumentException("UnpackPrefabInstance must be called with a root Prefab instance GameObject."); if (action == InteractionMode.UserAction) { var undoActionName = "Unpack Prefab instance"; - Undo.RegisterFullObjectHierarchyUndo(root, undoActionName); - var instanceRoots = UnpackPrefabInstanceAndReturnNewOutermostRoots(root, unpackMode); - foreach (var instanceRoot in instanceRoots) + Undo.RegisterFullObjectHierarchyUndo(instanceRoot, undoActionName); + var newInstanceRoots = UnpackPrefabInstanceAndReturnNewOutermostRoots(instanceRoot, unpackMode); + foreach (var newInstanceRoot in newInstanceRoots) { - var prefabInstance = PrefabUtility.GetPrefabInstanceHandle(instanceRoot); + var prefabInstance = PrefabUtility.GetPrefabInstanceHandle(newInstanceRoot); if (prefabInstance) { Undo.RegisterCreatedObjectUndo(prefabInstance, undoActionName); @@ -1347,28 +1414,10 @@ public static void UnpackPrefabInstance(GameObject root, PrefabUnpackMode unpack } else { - UnpackPrefabInstanceAndReturnNewOutermostRoots(root, unpackMode); + UnpackPrefabInstanceAndReturnNewOutermostRoots(instanceRoot, unpackMode); } } - public static bool IsPartOfImmutablePrefab(Object gameObjectOrComponent) - { - if (IsPartOfModelPrefab(gameObjectOrComponent)) - return true; - - // If prefab instance, get the prefab asset. - if (!EditorUtility.IsPersistent(gameObjectOrComponent)) - gameObjectOrComponent = GetCorrespondingObjectFromSource(gameObjectOrComponent); - - string prefabAssetPath = AssetDatabase.GetAssetPath(gameObjectOrComponent); - bool isRootFolder, isReadonly; - bool validPath = AssetDatabase.GetAssetFolderInfo(prefabAssetPath, out isRootFolder, out isReadonly); - if (validPath && isReadonly) - return true; - - return false; - } - internal static bool HasInvalidComponent(Object gameObjectOrComponent) { if (gameObjectOrComponent == null) @@ -1447,13 +1496,13 @@ public static GameObject LoadPrefabContents(string assetPath) return roots[0]; } - public static void UnloadPrefabContents(GameObject root) + public static void UnloadPrefabContents(GameObject contentsRoot) { - if (!EditorSceneManager.IsPreviewSceneObject(root)) + if (!EditorSceneManager.IsPreviewSceneObject(contentsRoot)) { throw new ArgumentException("Specified object is not part of Prefab contents"); } - var scene = root.scene; + var scene = contentsRoot.scene; EditorSceneManager.ClosePreviewScene(scene); } diff --git a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs index b99d96e3a3..50bfeaebfe 100644 --- a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs +++ b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs @@ -37,6 +37,12 @@ internal static class Constants internal class Styles { public static readonly GUIContent browse = EditorGUIUtility.TrTextContent("Browse..."); + public static readonly GUIStyle clearBindingButton = new GUIStyle(GUI.skin.button); + + static Styles() + { + clearBindingButton.margin.top = 0; + } } internal class GeneralProperties @@ -668,7 +674,7 @@ private void CheckForCollisions(Event e, ShortcutEntry selectedShortcut, PrefKey // Check shortcuts shortcutController.directory.FindShortcutEntries( - new List {new KeyCombination(e)}, + new List {KeyCombination.FromKeyboardInput(e)}, new[] { context }, collisions); @@ -711,6 +717,9 @@ private void ShowShortcutConfiguration(int controlID, ShortcutEntry selectedShor { GUILayout.Label("Key:"); e = EditorGUILayout.KeyEventField(e); + + if (GUILayout.Button("Clear", Styles.clearBindingButton)) + e.keyCode = KeyCode.None; } using (new GUILayout.HorizontalScope()) @@ -732,7 +741,9 @@ private void ShowShortcutConfiguration(int controlID, ShortcutEntry selectedShor if (m_ValidKeyChange) { // TODO: Don't clobber secondary+ combinations - var newCombination = new List { new KeyCombination(e) }; + var newCombination = new List(); + if (e.keyCode != KeyCode.None) + newCombination.Add(KeyCombination.FromKeyboardInput(e)); shortcutController.profileManager.ModifyShortcutEntry(selectedShortcut.identifier, newCombination); shortcutController.profileManager.PersistChanges(); } diff --git a/Editor/Mono/ProjectBrowser.cs b/Editor/Mono/ProjectBrowser.cs index 513ebb2d6e..6229ff1dda 100644 --- a/Editor/Mono/ProjectBrowser.cs +++ b/Editor/Mono/ProjectBrowser.cs @@ -1277,6 +1277,13 @@ void AssetTreeKeyboardInputCallback() OpenAssetSelection(Selection.instanceIDs); } break; + case KeyCode.Delete: + if (Event.current.shift) + { + DeleteSelectedAssets(false); + Event.current.Use(); + } + break; } } } @@ -2266,7 +2273,7 @@ void AssetLabelsDropDown() Rect r = GUILayoutUtility.GetRect(s_Styles.m_FilterByLabel, EditorStyles.toolbarButton); if (EditorGUI.DropdownButton(r, s_Styles.m_FilterByLabel, FocusType.Passive, EditorStyles.toolbarButton)) { - PopupWindow.Show(r, new PopupList(m_AssetLabels), null, ShowMode.PopupMenuWithKeyboardFocus); + PopupWindow.Show(r, new PopupList(m_AssetLabels)); } } diff --git a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs index f1543afe72..510893410e 100644 --- a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs +++ b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs @@ -12,6 +12,7 @@ using UnityEditor.SceneManagement; using UnityEditorInternal; using UnityEditor.Experimental; +using UnityEditor.Utils; using UnityEngine; using UnityEngine.Internal; using Object = UnityEngine.Object; @@ -107,15 +108,6 @@ public override void Action(int instanceId, string pathName, string resourceFile } } - internal class DoCreatePrefab : EndNameEditAction - { - public override void Action(int instanceId, string pathName, string resourceFile) - { - Object o = PrefabUtility.CreateEmptyPrefab(pathName); - ProjectWindowUtil.ShowCreatedAsset(o); - } - } - internal class DoCreatePrefabVariant : EndNameEditAction { public override void Action(int instanceId, string pathName, string resourceFile) @@ -257,12 +249,6 @@ public static void CreateScene() StartNameEditingIfProjectWindowExists(0, ScriptableObject.CreateInstance(), "New Scene.unity", EditorGUIUtility.FindTexture(typeof(SceneAsset)), null); } - // Create a prefab - public static void CreatePrefab() - { - StartNameEditingIfProjectWindowExists(0, ScriptableObject.CreateInstance(), "New Prefab.prefab", EditorGUIUtility.IconContent("Prefab Icon").image as Texture2D, null); - } - [MenuItem("Assets/Create/Prefab Variant", true)] static bool CreatePrefabVariantValidation() { @@ -279,7 +265,7 @@ static void CreatePrefabVariant() string sourcePath = AssetDatabase.GetAssetPath(go); - string sourceDir = System.IO.Path.GetDirectoryName(sourcePath); + string sourceDir = Path.GetDirectoryName(sourcePath).ConvertSeparatorsToUnity(); string variantPath = string.Format("{0}/{1} Variant.prefab", sourceDir, go.name); StartNameEditingIfProjectWindowExists( diff --git a/Editor/Mono/SceneHierarchy.cs b/Editor/Mono/SceneHierarchy.cs index dec6f48fed..3534c68484 100644 --- a/Editor/Mono/SceneHierarchy.cs +++ b/Editor/Mono/SceneHierarchy.cs @@ -485,8 +485,6 @@ public virtual void OnEnable() EditorSceneManager.newSceneCreated += OnSceneCreated; EditorSceneManager.sceneOpened += OnSceneOpened; - DoPingRequest(); - m_AllowAlphaNumericalSort = EditorPrefs.GetBool("AllowAlphaNumericHierarchy", false) || !InternalEditorUtility.isHumanControllingUs; // Always allow alphasorting when running automated tests so we can test alpha sorting SetUpSortMethodLists(); diff --git a/Editor/Mono/SceneHierarchyStageHandling.cs b/Editor/Mono/SceneHierarchyStageHandling.cs index a633173992..91c5ef2645 100644 --- a/Editor/Mono/SceneHierarchyStageHandling.cs +++ b/Editor/Mono/SceneHierarchyStageHandling.cs @@ -29,7 +29,7 @@ static Styles() SceneHierarchy m_SceneHierarchy; SceneHierarchyWindow m_SceneHierarchyWindow; - StateCache m_StateCache = new StateCache("Library/StateCache/Hierarchy/"); + StateCache m_StateCache = new StateCache("Library/StateCache/Hierarchy/"); GUIContent m_PrefabHeaderContent; bool m_LastPrefabStageModifiedState; @@ -105,8 +105,30 @@ void HandleFirstTimePrefabStageIsOpened(StageNavigationItem stage) { if (stage.isPrefabStage && GetStoredHierarchyState(m_SceneHierarchyWindow, stage) == null) { - var visibleRootID = stage.prefabStage.prefabContentsRoot.GetInstanceID(); - m_SceneHierarchy.SetExpandedRecursive(visibleRootID, true); + SetDefaultExpandedStateForOpenedPrefab(stage.prefabStage.prefabContentsRoot); + } + } + + void SetDefaultExpandedStateForOpenedPrefab(GameObject root) + { + var expandedIDs = new List(); + AddParentsBelowButIgnoreNestedPrefabsRecursive(root.transform, expandedIDs); + expandedIDs.Sort(); + m_SceneHierarchy.treeViewState.expandedIDs = expandedIDs; + } + + void AddParentsBelowButIgnoreNestedPrefabsRecursive(Transform transform, List gameObjectInstanceIDs) + { + gameObjectInstanceIDs.Add(transform.gameObject.GetInstanceID()); + + int count = transform.childCount; + for (int i = 0; i < count; ++i) + { + var child = transform.GetChild(i); + if (child.childCount > 0 && !PrefabUtility.IsAnyPrefabInstanceRoot(child.gameObject)) + { + AddParentsBelowButIgnoreNestedPrefabsRecursive(child, gameObjectInstanceIDs); + } } } @@ -120,6 +142,13 @@ static DragAndDropVisualMode PrefabModeDraggingHandler(GameObjectTreeViewItem pa if (parentItem != null && parentItem.parent == null && dropPos != TreeViewDragging.DropPosition.Upon) return DragAndDropVisualMode.Rejected; + // Disallow dragging scenes into the hierarchy when it is in Prefab Mode (we do not support multi-scenes for prefabs yet) + foreach (var dragged in DragAndDrop.objectReferences) + { + if (dragged is SceneAsset) + return DragAndDropVisualMode.Rejected; + } + // Check for cyclic nesting (only on perform since it is an expensive operation) if (perform) { @@ -158,7 +187,7 @@ void SaveHierarchyState(SceneHierarchyWindow hierarchyWindow, StageNavigationIte if (stage == null) return; string key = StageUtility.CreateWindowAndStageIdentifier(hierarchyWindow.windowGUID, stage); - var state = m_StateCache.GetState(key); + var state = m_StateCache.GetState(key); if (state == null) state = new SceneHierarchyState(); state.SaveStateFromHierarchy(hierarchyWindow, stage); @@ -168,7 +197,7 @@ void SaveHierarchyState(SceneHierarchyWindow hierarchyWindow, StageNavigationIte SceneHierarchyState GetStoredHierarchyState(SceneHierarchyWindow hierarchyWindow, StageNavigationItem stage) { string key = StageUtility.CreateWindowAndStageIdentifier(hierarchyWindow.windowGUID, stage); - return m_StateCache.GetState(key); + return m_StateCache.GetState(key); } void LoadHierarchyState(SceneHierarchyWindow hierarchy, StageNavigationItem stage) diff --git a/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs b/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs index 3bee757397..0b3f63498a 100644 --- a/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs +++ b/Editor/Mono/SceneManagement/StageManager/PrefabStage/PrefabStage.cs @@ -38,6 +38,8 @@ public class PrefabStage HideFlagUtility m_HideFlagUtility; Texture2D m_PrefabFileIcon; bool m_TemporarilyDisableAutoSave; + float m_LastSavingDuration = 0f; + const float kDurationBeforeShowingSavingBadge = 1.0f; bool m_AnalyticsDidUserModify; bool m_AnalyticsDidUserSave; @@ -61,7 +63,7 @@ public GameObject prefabContentsRoot get { if (m_PrefabContentsRoot == null) - throw new InvalidOperationException("Requesting 'prefabContentsRoot' from Awake and OnEnable are not supported"); // The preview scene is not fully loaded when we call Awake and OnEnable on user scripts + throw new InvalidOperationException("Requesting 'prefabContentsRoot' from Awake and OnEnable are not supported"); // The prefab stage's m_PrefabContentsRoot is not yet set when we call Awake and OnEnable on user scripts when loading a prefab return m_PrefabContentsRoot; } } @@ -91,6 +93,12 @@ public string prefabAssetPath get { return m_PrefabAssetPath; } } + internal bool showingSavingLabel + { + get; + private set; + } + internal Texture2D prefabFileIcon { get { return m_PrefabFileIcon; } @@ -261,14 +269,14 @@ bool isTextFieldCaretShowing bool readyToAutoSave { - get { return m_PrefabContentsRoot != null && HasSceneBeenModified() && GUIUtility.hotControl == 0 && !isTextFieldCaretShowing; } + get { return m_PrefabContentsRoot != null && HasSceneBeenModified() && GUIUtility.hotControl == 0 && !isTextFieldCaretShowing && !EditorApplication.isCompiling; } } void HandleAutoSave() { if (autoSave && readyToAutoSave) { - SavePrefabWithVersionControlDialogAndRenameDialog(); + AutoSave(); } } @@ -298,7 +306,7 @@ public void ClearDirtiness() m_InitialSceneDirtyID = m_PreviewScene.dirtyID; } - // Not private so we use it in Tests + // Is internal so we can use it in Tests internal void SavePrefab() { if (!initialized) @@ -313,14 +321,22 @@ internal void SavePrefab() if (prefabSaving != null) prefabSaving(m_PrefabContentsRoot); - ClearDirtiness(); - PrefabUtility.SaveAsPrefabAsset(m_PrefabContentsRoot, m_PrefabAssetPath); + var startTime = EditorApplication.timeSinceStartup; + var prefabAssetRoot = PrefabUtility.SaveAsPrefabAsset(m_PrefabContentsRoot, m_PrefabAssetPath); + m_LastSavingDuration = (float)(EditorApplication.timeSinceStartup - startTime); + + if (prefabAssetRoot != null) + { + ClearDirtiness(); - if (prefabSaved != null) - prefabSaved(m_PrefabContentsRoot); + if (prefabSaved != null) + prefabSaved(m_PrefabContentsRoot); + } if (SceneHierarchy.s_DebugPrefabStage) Debug.Log("SAVE PREFAB ended"); + + showingSavingLabel = false; } internal bool SaveAsNewPrefab(string newPath, bool asCopy) @@ -390,35 +406,53 @@ internal bool SaveAsNewPrefabWithSavePanel() return SaveAsNewPrefab(relativePath, false); } - // Returns true if prefab was saved. - internal bool SavePrefabWithVersionControlDialogAndRenameDialog() + void PerformDelayedAutoSave() { - Assert.IsTrue(m_PrefabContentsRoot != null, "We should have a valid m_PrefabContentsRoot when saving to prefab asset"); - bool editablePrefab = !AnimationMode.InAnimationMode(); + EditorApplication.update -= PerformDelayedAutoSave; + SavePrefabWithVersionControlDialogAndRenameDialog(); + } - //bool editablePrefab = UnityEditor.VersionControl.Provider.PromptAndCheckoutIfNeeded( - // new string[] {context.assetPath}, - // "The version control requires you to check out the prefab before applying changes."); + void AutoSave() + { + showingSavingLabel = m_LastSavingDuration > kDurationBeforeShowingSavingBadge; + if (showingSavingLabel) + { + // Save delayed if we want to show the save badge while saving. + foreach (SceneView sceneView in SceneView.sceneViews) + sceneView.Repaint(); - if (editablePrefab) + EditorApplication.update += PerformDelayedAutoSave; + } + else { - if (!PrefabUtility.PromptAndCheckoutPrefabIfNeeded(m_PrefabAssetPath, PrefabUtility.SaveVerb.Save)) - { - // If user doesn't want to check out prefab asset, or it cannot be, - // it doesn't make sense to keep auto save on. - m_TemporarilyDisableAutoSave = true; - return false; - } + // Save directly if we don't want to show the saving badge + SavePrefabWithVersionControlDialogAndRenameDialog(); + } + } - bool showCancelButton = !autoSave; - if (!CheckRenamedPrefabRootWhenSaving(showCancelButton)) - return false; + // Returns true if prefab was saved. + internal bool SavePrefabWithVersionControlDialogAndRenameDialog() + { + if (m_PrefabContentsRoot == null) + { + Debug.LogError("We should have a valid m_PrefabContentsRoot when saving to prefab asset"); + return false; + } - SavePrefab(); - return true; + if (!PrefabUtility.PromptAndCheckoutPrefabIfNeeded(m_PrefabAssetPath, PrefabUtility.SaveVerb.Save)) + { + // If user doesn't want to check out prefab asset, or it cannot be, + // it doesn't make sense to keep auto save on. + m_TemporarilyDisableAutoSave = true; + return false; } - return false; + bool showCancelButton = !autoSave; + if (!CheckRenamedPrefabRootWhenSaving(showCancelButton)) + return false; + + SavePrefab(); + return true; } // Returns true if we should continue saving diff --git a/Editor/Mono/SceneManagement/StageManager/StageNavigationManager.cs b/Editor/Mono/SceneManagement/StageManager/StageNavigationManager.cs index e23f0caa92..e13f91687a 100644 --- a/Editor/Mono/SceneManagement/StageManager/StageNavigationManager.cs +++ b/Editor/Mono/SceneManagement/StageManager/StageNavigationManager.cs @@ -265,10 +265,6 @@ internal void NavigateBack(Analytics.ChangeType stageChangeAnalytics) var previousStage = m_NavigationHistory.GetPrevious(); SwitchToStage(previousStage, false, true, stageChangeAnalytics); } - else - { - Debug.LogError("Cannot navigate back"); - } } internal void GoToMainStage(bool setPreviousSelection, Analytics.ChangeType stageChangeAnalytics) @@ -469,8 +465,8 @@ static bool OnOpenAsset(int instanceID, int line) if (assetPath.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { - // The 'line' parameter is used for passing an instance if entered from the Hierarchy or SceneView, otherwise it is -1 - GameObject instanceRoot = line == -1 ? null : (GameObject)EditorUtility.InstanceIDToObject(line); + // The 'line' parameter can be used for passing an instanceID of a prefab instance + GameObject instanceRoot = line == -1 ? null : EditorUtility.InstanceIDToObject(line) as GameObject; PrefabStageUtility.OpenPrefab(assetPath, instanceRoot, Analytics.ChangeType.EnterViaAssetOpened); return true; diff --git a/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs b/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs index b3f4b6ffa8..c546825ccd 100644 --- a/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs +++ b/Editor/Mono/SceneModeWindows/DefaultLightingExplorerExtension.cs @@ -14,7 +14,7 @@ private static class Styles public static readonly GUIContent[] ProjectionStrings = { EditorGUIUtility.TrTextContent("Infinite"), EditorGUIUtility.TrTextContent("Box") }; public static readonly GUIContent[] LightmapEmissiveStrings = { EditorGUIUtility.TrTextContent("Realtime"), EditorGUIUtility.TrTextContent("Baked") }; public static readonly GUIContent Name = EditorGUIUtility.TrTextContent("Name"); - public static readonly GUIContent On = EditorGUIUtility.TrTextContent("On"); + public static readonly GUIContent Enabled = EditorGUIUtility.TrTextContent("Enabled"); public static readonly GUIContent Type = EditorGUIUtility.TrTextContent("Type"); public static readonly GUIContent Shape = EditorGUIUtility.TrTextContent("Shape"); public static readonly GUIContent Mode = EditorGUIUtility.TrTextContent("Mode"); @@ -69,8 +69,8 @@ protected virtual LightingExplorerTableColumn[] GetLightColumns() { return new[] { - new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Name, Styles.Name, null, 200), // 0: Name - new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Checkbox, Styles.On, "m_Enabled", 25), // 1: Enabled + new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Checkbox, Styles.Enabled, "m_Enabled", 50), // 0: Enabled + new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Name, Styles.Name, null, 200), // 1: Name new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Enum, Styles.Type, "m_Type", 120, (r, prop, dep) => { // To the user, we will only display it as a area light, but under the hood, we have Rectangle and Disc. This is not to confuse people @@ -168,8 +168,8 @@ protected virtual LightingExplorerTableColumn[] GetReflectionProbeColumns() { return new[] { - new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Name, Styles.Name, null, 200), // 0: Name - new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Checkbox, Styles.On, "m_Enabled", 25), // 1: Enabled + new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Checkbox, Styles.Enabled, "m_Enabled", 50), // 0: Enabled + new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Name, Styles.Name, null, 200), // 1: Name new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Int, Styles.Mode, "m_Mode", 70, (r, prop, dep) => { EditorGUI.IntPopup(r, prop, ReflectionProbeEditor.Styles.reflectionProbeMode, ReflectionProbeEditor.Styles.reflectionProbeModeValues, GUIContent.none); @@ -195,8 +195,8 @@ protected virtual LightingExplorerTableColumn[] GetLightProbeColumns() { return new[] { - new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Name, Styles.Name, null, 200), // 0: Name - new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Checkbox, Styles.On, "m_Enabled", 25), // 1: Enabled + new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Checkbox, Styles.Enabled, "m_Enabled", 50), // 0: Enabled + new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Name, Styles.Name, null, 200), // 1: Name }; } diff --git a/Editor/Mono/SceneModeWindows/LightingWindowBakeSettings.cs b/Editor/Mono/SceneModeWindows/LightingWindowBakeSettings.cs index 79ac29a750..3479ab3321 100644 --- a/Editor/Mono/SceneModeWindows/LightingWindowBakeSettings.cs +++ b/Editor/Mono/SceneModeWindows/LightingWindowBakeSettings.cs @@ -71,7 +71,6 @@ internal class LightingWindowBakeSettings SerializedProperty m_PVRFilteringAtrousPositionSigmaAO; SerializedProperty m_BounceScale; - SerializedProperty m_UpdateThreshold; static bool PlayerHasSM20Support() { @@ -131,7 +130,6 @@ private void InitSettings() //dev debug properties m_BounceScale = so.FindProperty("m_GISettings.m_BounceScale"); - m_UpdateThreshold = so.FindProperty("m_GISettings.m_TemporalCoherenceThreshold"); } public void OnEnable() @@ -351,7 +349,6 @@ public void DeveloperBuildSettingsGUI() Lightmapping.filterMode = (FilterMode)EditorGUILayout.EnumPopup(EditorGUIUtility.TempContent("Filter Mode"), Lightmapping.filterMode); EditorGUILayout.Slider(m_BounceScale, 0.0f, 10.0f, Styles.BounceScale); - EditorGUILayout.Slider(m_UpdateThreshold, 0.0f, 1.0f, Styles.UpdateThreshold); if (GUILayout.Button("Clear disk cache", GUILayout.Width(LightingWindow.kButtonWidth))) { @@ -371,6 +368,16 @@ public void DeveloperBuildSettingsGUI() DynamicGI.UpdateEnvironment(); } + private void ClampFilterType(SerializedProperty filter) + { + if (LightmapEditorSettings.lightmapper == LightmapEditorSettings.Lightmapper.ProgressiveGPU) + { + // Force unsupported A-Trous filter back to Gaussian. + if (filter.intValue == (int)LightmapEditorSettings.FilterType.ATrous) + filter.intValue = (int)LightmapEditorSettings.FilterType.Gaussian; + } + } + void GeneralLightmapSettingsGUI() { bool bakedGISupported = SupportedRenderingFeatures.IsLightmapBakeTypeSupported(LightmapBakeType.Baked); @@ -390,11 +397,10 @@ void GeneralLightmapSettingsGUI() { EditorGUI.BeginChangeCheck(); - //TODO(RadeonRays) Remove this when GPU lightmapper is public. - - if (Unsupported.IsDeveloperMode() && (Application.platform == RuntimePlatform.WindowsEditor)) + //TODO(RadeonRays): Remove this when GPU lightmapper works on macOS and Linux. + if (Application.platform != RuntimePlatform.WindowsEditor) { - var backendOptions = new[] { "Enlighten", "Progressive CPU", "Progressive GPU (Preview)" }; + var backendOptions = new[] { "Enlighten", "Progressive CPU" }; m_BakeBackend.intValue = EditorGUILayout.Popup(Styles.BakeBackend, m_BakeBackend.intValue, backendOptions); } else @@ -453,7 +459,15 @@ void GeneralLightmapSettingsGUI() { EditorGUI.indentLevel++; - EditorGUILayout.PropertyField(m_PVRFilterTypeDirect, Styles.PVRFilterTypeDirect); + if (LightmapEditorSettings.lightmapper == LightmapEditorSettings.Lightmapper.ProgressiveGPU) + EditorGUILayout.HelpBox(Styles.ProgressiveGPUWarning.text, MessageType.Info); + + ClampFilterType(m_PVRFilterTypeDirect); + if (LightmapEditorSettings.lightmapper == LightmapEditorSettings.Lightmapper.ProgressiveGPU) + EditorGUILayout.IntPopup(m_PVRFilterTypeDirect, Styles.GPUFilterOptions, Styles.GPUFilterInts, Styles.PVRFilterTypeDirect); + else + EditorGUILayout.PropertyField(m_PVRFilterTypeDirect, Styles.PVRFilterTypeDirect); + DrawFilterSettingField(m_PVRFilteringGaussRadiusDirect, m_PVRFilteringAtrousPositionSigmaDirect, Styles.PVRFilteringGaussRadiusDirect, @@ -462,7 +476,11 @@ void GeneralLightmapSettingsGUI() EditorGUILayout.Space(); - EditorGUILayout.PropertyField(m_PVRFilterTypeIndirect, Styles.PVRFilterTypeIndirect); + if (LightmapEditorSettings.lightmapper == LightmapEditorSettings.Lightmapper.ProgressiveGPU) + EditorGUILayout.IntPopup(m_PVRFilterTypeIndirect, Styles.GPUFilterOptions, Styles.GPUFilterInts, Styles.PVRFilterTypeIndirect); + else + EditorGUILayout.PropertyField(m_PVRFilterTypeIndirect, Styles.PVRFilterTypeIndirect); + ClampFilterType(m_PVRFilterTypeIndirect); DrawFilterSettingField(m_PVRFilteringGaussRadiusIndirect, m_PVRFilteringAtrousPositionSigmaIndirect, Styles.PVRFilteringGaussRadiusIndirect, @@ -472,8 +490,11 @@ void GeneralLightmapSettingsGUI() using (new EditorGUI.DisabledScope(!m_AmbientOcclusion.boolValue)) { EditorGUILayout.Space(); - - EditorGUILayout.PropertyField(m_PVRFilterTypeAO, Styles.PVRFilterTypeAO); + if (LightmapEditorSettings.lightmapper == LightmapEditorSettings.Lightmapper.ProgressiveGPU) + EditorGUILayout.IntPopup(m_PVRFilterTypeAO, Styles.GPUFilterOptions, Styles.GPUFilterInts, Styles.PVRFilterTypeAO); + else + EditorGUILayout.PropertyField(m_PVRFilterTypeAO, Styles.PVRFilterTypeAO); + ClampFilterType(m_PVRFilterTypeAO); DrawFilterSettingField(m_PVRFilteringGaussRadiusAO, m_PVRFilteringAtrousPositionSigmaAO, Styles.PVRFilteringGaussRadiusAO, Styles.PVRFilteringAtrousPositionSigmaAO, @@ -688,6 +709,10 @@ static class Styles public static readonly GUIContent PVRFilteringAtrousPositionSigmaIndirect = EditorGUIUtility.TrTextContent("Indirect Sigma", "Controls the threshold of the filter for indirect light stored in the lightmap. A higher value will increase the threshold, reducing noise in the indirect layer of the lightmap. Too high of a value can cause a loss of detail in the lightmap."); public static readonly GUIContent PVRFilteringAtrousPositionSigmaAO = EditorGUIUtility.TrTextContent("Ambient Occlusion Sigma", "Controls the threshold of the filter for ambient occlusion stored in the lightmap. A higher value will increase the threshold, reducing noise in the ambient occlusion layer of the lightmap. Too high of a value can cause a loss of detail in the lightmap."); public static readonly GUIContent PVRCulling = EditorGUIUtility.TrTextContent("Prioritize View", "Specifies whether the lightmapper should prioritize baking texels within the scene view. When disabled, objects outside the scene view will have the same priority as those in the scene view."); + // TODO(RadeonRays): Used for hiding A-trous filtering option until it is implemented. + public static readonly GUIContent[] GPUFilterOptions = new[] { EditorGUIUtility.TrTextContent("Gaussian"), EditorGUIUtility.TrTextContent("None") }; + public static readonly int[] GPUFilterInts = new[] { (int)LightmapEditorSettings.FilterType.Gaussian, (int)LightmapEditorSettings.FilterType.None }; + public static readonly GUIContent ProgressiveGPUWarning = EditorGUIUtility.TrTextContent("A-Trous filtering is not implemented in the Progressive GPU lightmapper yet. Use the CPU lightmapper instead if you need this functionality."); } } } // namespace diff --git a/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs b/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs index c07d539564..c80cb9ee41 100644 --- a/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs +++ b/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs @@ -31,10 +31,13 @@ internal class LightingWindowLightmapPreviewTab SerializedProperty m_LightingDataAsset; SerializedProperty m_TextureCompression; - class Styles + static class Styles { public static readonly GUIStyle SelectedLightmapHighlight = "LightmapEditorSelectedHighlight"; public static readonly GUIContent LightingDataAsset = EditorGUIUtility.TrTextContent("Lighting Data Asset", "A different LightingData.asset can be assigned here. These assets are generated by baking a scene in the OnDemand mode."); + public static readonly GUIContent OpenPreview = EditorGUIUtility.TrTextContent("Open Preview"); + + public static readonly GUIStyle OpenPreviewStyle = EditorStyles.objectFieldThumb.name + "LightmapPreviewOverlay"; } public LightingWindowLightmapPreviewTab(LightmapType type) @@ -171,28 +174,27 @@ private void LightmapListGUI(LightmapData[] lightmaps, VisualisationGITexture[] private void LightmapField(Texture2D lightmap, int index, Hash128 hash) { Rect rect = GUILayoutUtility.GetRect(100, 100, EditorStyles.objectField); + Rect buttonRect = new Rect(rect.xMax - 70, rect.yMax - 14, 70, 14); if (EditorGUI.Toggle(rect, index == m_SelectedLightmapIndex, EditorStyles.objectFieldThumb)) { m_SelectedLightmapIndex = index; - if (rect.Contains(Event.current.mousePosition)) + if ((buttonRect.Contains(Event.current.mousePosition) && Event.current.clickCount == 1) || + (rect.Contains(Event.current.mousePosition) && Event.current.clickCount == 2)) + { + if (isRealtimeLightmap) + LightmapPreviewWindow.CreateLightmapPreviewWindow(m_SelectedLightmapIndex, true, true); + else + LightmapPreviewWindow.CreateLightmapPreviewWindow(m_SelectedLightmapIndex, false, true); + } + else if (rect.Contains(Event.current.mousePosition) && Event.current.clickCount == 1) { Object actualTargetObject = lightmap; Component com = actualTargetObject as Component; if (com) actualTargetObject = com.gameObject; - - if (Event.current.clickCount == 1) - EditorGUI.PingObjectOrShowPreviewOnClick(actualTargetObject, GUILayoutUtility.GetLastRect()); - - if (Event.current.clickCount == 2) - { - if (isRealtimeLightmap) - LightmapPreviewWindow.CreateLightmapPreviewWindow(m_SelectedLightmapIndex, true, true); - else - LightmapPreviewWindow.CreateLightmapPreviewWindow(m_SelectedLightmapIndex, false, true); - } + EditorGUI.PingObjectOrShowPreviewOnClick(actualTargetObject, GUILayoutUtility.GetLastRect()); } } @@ -201,6 +203,8 @@ private void LightmapField(Texture2D lightmap, int index, Hash128 hash) rect = EditorStyles.objectFieldThumb.padding.Remove(rect); EditorGUI.DrawPreviewTexture(rect, lightmap); + Styles.OpenPreviewStyle.Draw(rect, Styles.OpenPreview, false, false, false, false); + if ((!isRealtimeLightmap && index == m_ActiveGameObjectLightmapIndex) || (isRealtimeLightmap && hash == m_ActiveGameObjectTextureHash)) { Styles.SelectedLightmapHighlight.Draw(rect, false, false, false, false); diff --git a/Editor/Mono/SceneModeWindows/LightmapPreviewWindow.cs b/Editor/Mono/SceneModeWindows/LightmapPreviewWindow.cs index 79645fd7f9..1bc8fb7a3a 100644 --- a/Editor/Mono/SceneModeWindows/LightmapPreviewWindow.cs +++ b/Editor/Mono/SceneModeWindows/LightmapPreviewWindow.cs @@ -37,7 +37,9 @@ internal class LightmapPreviewWindow : EditorWindow Hash128 m_RealtimeTextureHash = new Hash128(); VisualisationGITexture m_CachedTexture; GameObject[] m_CachedTextureObjects; + int m_ActiveGameObjectLightmapIndex = -1; // the object the user selects in the scene + int m_ActiveGameObjectInstanceId = -1; // for instance based non-atlas textures such as baked emissive for Progressive Hash128 m_ActiveGameObjectTextureHash = new Hash128(); // the object the user selects in the scene private float m_ExposureSliderMax = 10f; @@ -93,19 +95,18 @@ static class Styles public static readonly GUIContent TextureNotAvailableRealtime = EditorGUIUtility.TrTextContent("The texture is not available at the moment."); public static readonly GUIContent TextureNotAvailableBaked = EditorGUIUtility.TrTextContent("The texture is not available at the moment.\nPlease try to rebake the current scene or turn on Auto, and make sure that this object is set to Lightmap Static if it's meant to be baked."); public static readonly GUIContent TextureNotAvailableBakedShadowmask = EditorGUIUtility.TrTextContent("The texture is not available at the moment.\nPlease make sure that Mixed Lights affect this GameObject and that it is set to Lightmap Static."); + public static readonly GUIContent TextureNotAvailableBakedAlbedoEmissive = EditorGUIUtility.TrTextContent("The texture is not an index based texture and is not available when using Progressive.\nPlease go to the instance you wish to debug, and select the lightmap on the Mesh Renderer."); public static readonly GUIContent TextureLoading = EditorGUIUtility.TrTextContent("Loading..."); public static readonly GUIContent ExposureIcon = EditorGUIUtility.TrIconContent("SceneViewLighting", "Controls the number of stops to over or under expose the lightmap."); } public int lightmapIndex { - get { return m_LightmapIndex; } set { m_LightmapIndex = value; } } public int instanceID { - get { return m_InstanceID; } set { m_InstanceID = value; } } @@ -115,17 +116,24 @@ public bool isRealtimeLightmap set { m_IsRealtimeLightmap = value; } } + // this seperates between lightsmaps that we opened from a specific index, or the ones that are connected to an object (where the index can change) private bool isIndexBased { - get { return m_LightmapIndex != -1; } + get { return m_InstanceID == -1; } } private string lightmapTitle { get { - return isIndexBased ? ((isRealtimeLightmap ? "Realtime Lightmap Index " : "Lightmap Index ") + lightmapIndex) : - (isRealtimeLightmap ? "Realtime Lightmap" : "Lightmap"); + if (isIndexBased) return ((isRealtimeLightmap ? "Realtime Lightmap Index " : "Lightmap Index ") + m_LightmapIndex); + + var obj = EditorUtility.InstanceIDToObject(m_InstanceID); + + if (obj) + return (isRealtimeLightmap ? "Realtime" : "") + " Lightmap for '" + obj.name + "'"; + + return (isRealtimeLightmap ? "Realtime" : "") + " Lightmap"; } } @@ -199,6 +207,7 @@ private void UpdateActiveGameObjectSelection() (terrain = Selection.activeGameObject.GetComponent()) == null)) { m_ActiveGameObjectLightmapIndex = -1; + m_ActiveGameObjectInstanceId = -1; m_ActiveGameObjectTextureHash = new Hash128(); return; } @@ -214,7 +223,10 @@ private void UpdateActiveGameObjectSelection() m_ActiveGameObjectTextureHash = new Hash128(); } else + { m_ActiveGameObjectLightmapIndex = renderer != null ? renderer.lightmapIndex : terrain.lightmapIndex; + m_ActiveGameObjectInstanceId = renderer != null ? renderer.GetInstanceID() : terrain.GetInstanceID(); + } } private void PreviewSettings() @@ -297,9 +309,11 @@ private void DrawPreview(Rect r) if (m_CachedTexture.textureAvailability == GITextureAvailability.GITextureNotAvailable || m_CachedTexture.textureAvailability == GITextureAvailability.GITextureUnknown) { - if (LightmapVisualizationUtility.IsBakedTextureType(textureType)) + if (!isRealtimeLightmap) { - if (textureType == GITextureType.BakedShadowMask) + if (!LightmapVisualizationUtility.IsAtlasTextureType(textureType) && isIndexBased) + GUI.Label(drawableArea, Styles.TextureNotAvailableBakedAlbedoEmissive, Styles.PreviewLabel); + else if (textureType == GITextureType.BakedShadowMask) GUI.Label(drawableArea, Styles.TextureNotAvailableBakedShadowmask, Styles.PreviewLabel); else GUI.Label(drawableArea, Styles.TextureNotAvailableBaked, Styles.PreviewLabel); @@ -324,8 +338,12 @@ private void DrawPreview(Rect r) case EventType.ValidateCommand: case EventType.ExecuteCommand: - if (Event.current.commandName == EventCommandNames.FrameSelected && (isRealtimeLightmap ? m_RealtimeTextureHash == m_ActiveGameObjectTextureHash : m_LightmapIndex == m_ActiveGameObjectLightmapIndex)) + if (Event.current.commandName == EventCommandNames.FrameSelected && IsSelectedObjectInLightmap(textureType)) { + // There are instance based baked textures where we don't get any STs and can't do the framing + if (!isRealtimeLightmap && !LightmapVisualizationUtility.IsAtlasTextureType(textureType)) + break; + Vector4 lightmapTilingOffset = LightmapVisualizationUtility.GetLightmapTilingOffset(lightmapType); Vector2 min = new Vector2(lightmapTilingOffset.z, lightmapTilingOffset.w); @@ -349,7 +367,7 @@ private void DrawPreview(Rect r) Rect rect = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); rect.width = rect.height = Mathf.Max(rect.width, rect.height); rect.x -= (offsetX * min.x); - rect.y += (offsetY * (1 - max.y)); + rect.y += (offsetY * (1 - max.y)); m_ZoomablePreview.shownArea = rect; Event.current.Use(); @@ -377,7 +395,7 @@ private void DrawPreview(Rect r) texture.filterMode = FilterMode.Point; LightmapVisualizationUtility.DrawTextureWithUVOverlay(texture, - (m_ShowUVOverlay && (isRealtimeLightmap ? m_RealtimeTextureHash == m_ActiveGameObjectTextureHash : m_LightmapIndex == m_ActiveGameObjectLightmapIndex)) ? Selection.activeGameObject : null, + (m_ShowUVOverlay && IsSelectedObjectInLightmap(textureType)) ? Selection.activeGameObject : null, m_ShowUVOverlay ? m_CachedTextureObjects : new GameObject[] {}, drawableArea, textureRect, textureType, exposure); texture.filterMode = prevMode; } @@ -395,6 +413,17 @@ private void SelectPreviewTextureIndex(object textureOption) m_SelectedPreviewTextureOptionIndex = Array.IndexOf(options, textureOption); } + private bool IsSelectedObjectInLightmap(GITextureType textureType) + { + if (isRealtimeLightmap) + return (m_ActiveGameObjectTextureHash == m_RealtimeTextureHash); + + if (LightmapVisualizationUtility.IsAtlasTextureType(textureType)) + return (m_ActiveGameObjectLightmapIndex == m_LightmapIndex); + + return (m_ActiveGameObjectInstanceId == m_InstanceID); + } + private GITextureType GetSelectedTextureType() { GUIContent[] options = isRealtimeLightmap ? Styles.RealtimePreviewTextureOptions : Styles.BakedPreviewTextureOptions; @@ -437,7 +466,7 @@ private void UpdateCachedTexture(GITextureType textureType) { Hash128 systemHash; - if (!LightmapEditorSettings.GetInputSystemHash(instanceID, out systemHash)) + if (!LightmapEditorSettings.GetInputSystemHash(m_InstanceID, out systemHash)) return; m_RealtimeTextureHash = systemHash; @@ -446,7 +475,7 @@ private void UpdateCachedTexture(GITextureType textureType) { int lightmapIndex; - if (!LightmapEditorSettings.GetLightmapIndex(instanceID, out lightmapIndex)) + if (!LightmapEditorSettings.GetLightmapIndex(m_InstanceID, out lightmapIndex)) return; m_LightmapIndex = lightmapIndex; @@ -454,14 +483,14 @@ private void UpdateCachedTexture(GITextureType textureType) } Hash128 contentHash = isRealtimeLightmap ? LightmapVisualizationUtility.GetRealtimeGITextureHash(m_RealtimeTextureHash, textureType) : - LightmapVisualizationUtility.GetBakedGITextureHash(m_LightmapIndex, 0, textureType); + LightmapVisualizationUtility.GetBakedGITextureHash(m_LightmapIndex, m_InstanceID, textureType); // if we need to fetch a new texture if (m_CachedTexture.texture == null || m_CachedTexture.type != textureType || m_CachedTexture.contentHash != contentHash || m_CachedTexture.contentHash == new Hash128()) { m_CachedTexture = isRealtimeLightmap ? LightmapVisualizationUtility.GetRealtimeGITexture(m_RealtimeTextureHash, textureType) : - LightmapVisualizationUtility.GetBakedGITexture(m_LightmapIndex, 0, textureType); + LightmapVisualizationUtility.GetBakedGITexture(m_LightmapIndex, m_InstanceID, textureType); } if (!m_ShowUVOverlay) @@ -474,8 +503,10 @@ private void UpdateCachedTexture(GITextureType textureType) if (isRealtimeLightmap) m_CachedTextureObjects = LightmapVisualizationUtility.GetRealtimeGITextureRenderers(m_RealtimeTextureHash); - else + else if (LightmapVisualizationUtility.IsAtlasTextureType(textureType)) m_CachedTextureObjects = LightmapVisualizationUtility.GetBakedGITextureRenderers(m_LightmapIndex); + else // if it's an instance based baked lightmap, we only have 1 object in it + m_CachedTextureObjects = new GameObject[] {}; } private Rect ResizeRectToFit(Rect rect, Rect to) diff --git a/Editor/Mono/SceneView/SceneView.cs b/Editor/Mono/SceneView/SceneView.cs index ab9c61444e..c7c84b2df4 100644 --- a/Editor/Mono/SceneView/SceneView.cs +++ b/Editor/Mono/SceneView/SceneView.cs @@ -84,7 +84,7 @@ public static SceneView lastActiveSceneView public static SceneView currentDrawingSceneView { get { return s_CurrentDrawingSceneView; } } static readonly PrefColor kSceneViewBackground = new PrefColor("Scene/Background", 0.278431f, 0.278431f, 0.278431f, 0); - static readonly PrefColor kSceneViewPrefabBackground = new PrefColor("Scene/Background for Prefabs", 0.132f, 0.231f, 0.330f, 0); + internal static readonly PrefColor kSceneViewPrefabBackground = new PrefColor("Scene/Background for Prefabs", 0.132f, 0.231f, 0.330f, 0); static readonly PrefColor kSceneViewWire = new PrefColor("Scene/Wireframe", 0.0f, 0.0f, 0.0f, 0.5f); static readonly PrefColor kSceneViewWireOverlay = new PrefColor("Scene/Wireframe Overlay", 0.0f, 0.0f, 0.0f, 0.25f); static readonly PrefColor kSceneViewSelectedOutline = new PrefColor("Scene/Selected Outline", 255.0f / 255.0f, 102.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f); @@ -412,6 +412,9 @@ public float cameraDistance static Shader s_ShowMipsShader; static Shader s_ShowTextureStreamingShader; static Shader s_AuraShader; + static Shader s_BuildFilterShader; + static Material s_FadeMaterial; + static Material s_ApplyFilterMaterial; static Texture2D s_MipColorsTexture; // Handle Dragging of stuff over scene view @@ -741,7 +744,7 @@ void ToolbarDisplayStateGUI() if (EditorGUI.DropdownButton(modeRect, modeContent, FocusType.Passive, EditorStyles.toolbarDropDown)) { Rect rect = GUILayoutUtility.topLevel.GetLast(); - PopupWindow.Show(rect, new SceneRenderModeWindow(this), null, ShowMode.PopupMenuWithKeyboardFocus); + PopupWindow.Show(rect, new SceneRenderModeWindow(this)); GUIUtility.ExitGUI(); } @@ -768,7 +771,7 @@ void ToolbarDisplayStateGUI() if (EditorGUI.DropdownButton(fxRightRect, GUIContent.none, FocusType.Passive, GUIStyle.none)) { Rect rect = GUILayoutUtility.topLevel.GetLast(); - PopupWindow.Show(rect, new SceneFXWindow(this), null, ShowMode.PopupMenuWithKeyboardFocus); + PopupWindow.Show(rect, new SceneFXWindow(this)); GUIUtility.ExitGUI(); } @@ -1317,31 +1320,7 @@ private void DoDrawCamera(Rect windowSpaceCameraRect, Rect groupSpaceCameraRect, if (UseSceneFiltering()) { if (evt.type == EventType.Repaint) - { - // First pass: Draw objects which do not meet the search filter with grayscale image effect. - Handles.EnableCameraFx(m_Camera, true); - - Handles.SetCameraFilterMode(m_Camera, Handles.CameraFilterMode.ShowRest); - - float fade = Mathf.Clamp01((float)(EditorApplication.timeSinceStartup - m_StartSearchFilterTime)); - Handles.DrawCamera(groupSpaceCameraRect, m_Camera, m_CameraMode.drawMode); - Handles.DrawCameraFade(m_Camera, fade); - - // Second pass: Draw aura for objects which do meet search filter, but are occluded. - Handles.EnableCameraFx(m_Camera, false); - Handles.SetCameraFilterMode(m_Camera, Handles.CameraFilterMode.ShowFiltered); - if (!s_AuraShader) - s_AuraShader = EditorGUIUtility.LoadRequired("SceneView/SceneViewAura.shader") as Shader; - m_Camera.SetReplacementShader(s_AuraShader, ""); - Handles.DrawCamera(groupSpaceCameraRect, m_Camera, m_CameraMode.drawMode); - - // Third pass: Draw objects which do meet filter normally - m_Camera.SetReplacementShader(m_ReplacementShader, m_ReplacementString); - Handles.DrawCamera(groupSpaceCameraRect, m_Camera, m_CameraMode.drawMode, gridParam); - - if (fade < 1) - Repaint(); - } + RenderFilteredScene(groupSpaceCameraRect); if (evt.type == EventType.Repaint) RenderTexture.active = null; @@ -1368,6 +1347,68 @@ private void DoDrawCamera(Rect windowSpaceCameraRect, Rect groupSpaceCameraRect, } } + void RenderFilteredScene(Rect groupSpaceCameraRect) + { + var oldRenderingPath = m_Camera.renderingPath; + + // First pass: Draw the scene normally in destination render texture, save color buffer for later + DoClearCamera(groupSpaceCameraRect); + Handles.DrawCamera(groupSpaceCameraRect, m_Camera, m_CameraMode.drawMode); + + var colorDesc = m_SceneTargetTexture.descriptor; + colorDesc.depthBufferBits = 0; + var colorRT = RenderTexture.GetTemporary(colorDesc); + colorRT.name = "SavedColorRT"; + Graphics.Blit(m_SceneTargetTexture, colorRT); + + // Second pass: Blit the scene faded out in the scene target texture + float fade = Mathf.Clamp01((float)(EditorApplication.timeSinceStartup - m_StartSearchFilterTime)); + if (!s_FadeMaterial) + s_FadeMaterial = EditorGUIUtility.LoadRequired("SceneView/SceneViewGrayscaleEffectFade.mat") as Material; + s_FadeMaterial.SetFloat("_Fade", fade); + Graphics.Blit(colorRT, m_SceneTargetTexture, s_FadeMaterial); + + // Third pass: Draw aura for objects which meet the search filter, but are occluded. Save color buffer for later. + m_Camera.renderingPath = RenderingPath.Forward; + if (!s_AuraShader) + s_AuraShader = EditorGUIUtility.LoadRequired("SceneView/SceneViewAura.shader") as Shader; + m_Camera.SetReplacementShader(s_AuraShader, ""); + Handles.SetCameraFilterMode(m_Camera, Handles.CameraFilterMode.ShowFiltered); + Handles.DrawCamera(groupSpaceCameraRect, m_Camera, m_CameraMode.drawMode); + + var fadedDesc = m_SceneTargetTexture.descriptor; + colorDesc.depthBufferBits = 0; + var fadedRT = RenderTexture.GetTemporary(fadedDesc); + fadedRT.name = "FadedColorRT"; + Graphics.Blit(m_SceneTargetTexture, fadedRT); + + // Fourth pass: Draw objects which do meet filter in a mask + RenderTexture.active = m_SceneTargetTexture; + GL.Clear(false, true, Color.clear); + + if (!s_BuildFilterShader) + s_BuildFilterShader = EditorGUIUtility.LoadRequired("SceneView/SceneViewBuildFilter.shader") as Shader; + m_Camera.SetReplacementShader(s_BuildFilterShader, ""); + Handles.DrawCamera(groupSpaceCameraRect, m_Camera, m_CameraMode.drawMode); + + // Final pass: Blit the faded scene where the mask isn't set + if (!s_ApplyFilterMaterial) + s_ApplyFilterMaterial = EditorGUIUtility.LoadRequired("SceneView/SceneViewApplyFilter.mat") as Material; + s_ApplyFilterMaterial.SetTexture("_MaskTex", m_SceneTargetTexture); + Graphics.Blit(fadedRT, colorRT, s_ApplyFilterMaterial); + Graphics.Blit(colorRT, m_SceneTargetTexture); + + RenderTexture.ReleaseTemporary(colorRT); + RenderTexture.ReleaseTemporary(fadedRT); + + // Reset camera + m_Camera.SetReplacementShader(m_ReplacementShader, m_ReplacementString); + m_Camera.renderingPath = oldRenderingPath; + + if (fade < 1) + Repaint(); + } + void SetupPBRValidation() { DrawCameraMode renderMode = m_CameraMode.drawMode; @@ -1398,8 +1439,13 @@ void DoClearCamera(Rect cameraRect) // for the skybox we always want to use the same FOV. float skyboxFOV = GetVerticalFOV(kPerspectiveFov); float realFOV = m_Camera.fieldOfView; + + var clearFlags = m_Camera.clearFlags; + if (GraphicsSettings.renderPipelineAsset != null) + m_Camera.clearFlags = CameraClearFlags.Color; m_Camera.fieldOfView = skyboxFOV; Handles.ClearCamera(cameraRect, m_Camera); + m_Camera.clearFlags = clearFlags; m_Camera.fieldOfView = realFOV; } @@ -2222,9 +2268,7 @@ void SetupCamera() if (Event.current.type == EventType.Repaint) { - bool enableImageEffects = false; - if (!UseSceneFiltering()) - enableImageEffects = m_CameraMode.drawMode == DrawCameraMode.Textured && sceneViewState.showImageEffects; + bool enableImageEffects = m_CameraMode.drawMode == DrawCameraMode.Textured && sceneViewState.showImageEffects; UpdateImageEffects(enableImageEffects); } diff --git a/Editor/Mono/SceneView/SceneViewStageHandling.cs b/Editor/Mono/SceneView/SceneViewStageHandling.cs index 9f45d1315c..e73661f5a2 100644 --- a/Editor/Mono/SceneView/SceneViewStageHandling.cs +++ b/Editor/Mono/SceneView/SceneViewStageHandling.cs @@ -16,7 +16,7 @@ namespace UnityEditor class SceneViewStageHandling { SceneView m_SceneView; - StateCache m_StateCache = new StateCache("Library/StateCache/SceneView/"); + StateCache m_StateCache = new StateCache("Library/StateCache/SceneView/"); public BreadcrumbBar m_BreadcrumbBar = new BreadcrumbBar(); public bool m_BreadcrumbInitialized; @@ -36,11 +36,13 @@ static bool autoSave static class Styles { - public static GUIContent autoSaveGUIContent = EditorGUIUtility.TrTextContent("Auto Save"); + public static GUIContent autoSaveGUIContent = EditorGUIUtility.TrTextContent("Auto Save", "When Auto Save is enabled, every change you make is automatically saved to the Prefab Asset. Disable Auto Save if you experience long import times."); public static GUIContent saveButtonContent = EditorGUIUtility.TrTextContent("Save"); public static GUIContent checkoutButtonContent = EditorGUIUtility.TrTextContent("Check Out"); + public static GUIContent autoSavingBadgeContent = EditorGUIUtility.TrTextContent("Auto Saving..."); public static GUIStyle saveToggle; public static GUIStyle button; + public static GUIStyle savingBadge = "Badge"; static Styles() { @@ -186,7 +188,7 @@ void SaveCameraState(SceneView sceneView, StageNavigationItem stage) if (stage == null) return; string key = StageUtility.CreateWindowAndStageIdentifier(sceneView.windowGUID, stage); - var state = m_StateCache.GetState(key); + var state = m_StateCache.GetState(key); if (state == null) state = new SceneViewCameraState(); state.SaveStateFromSceneView(sceneView); @@ -196,7 +198,7 @@ void SaveCameraState(SceneView sceneView, StageNavigationItem stage) SceneViewCameraState GetStoredCameraState(SceneView sceneView, StageNavigationItem stage) { string key = StageUtility.CreateWindowAndStageIdentifier(sceneView.windowGUID, stage); - return m_StateCache.GetState(key); + return m_StateCache.GetState(key); } void LoadCameraState(SceneView sceneView, StageNavigationItem stage) @@ -232,16 +234,14 @@ void AutoSaveButtons() { StatusQueryOptions opts = EditorUserSettings.allowAsyncStatusUpdate ? StatusQueryOptions.UseCachedAsync : StatusQueryOptions.UseCachedIfPossible; bool openForEdit = AssetDatabase.IsOpenForEdit(item.prefabAssetPath, opts); - if (!openForEdit) + + PrefabStage stage = item.prefabStage; + if (stage.showingSavingLabel) { - if (GUILayout.Button(Styles.checkoutButtonContent, Styles.button)) - { - Task task = Provider.Checkout(AssetDatabase.LoadAssetAtPath(item.prefabAssetPath), CheckoutMode.Both); - task.Wait(); - } + GUILayout.Label(Styles.autoSavingBadgeContent, Styles.savingBadge); + GUILayout.Space(4); } - PrefabStage stage = item.prefabStage; if (!stage.autoSave) { using (new EditorGUI.DisabledScope(!openForEdit || !PrefabStageUtility.GetCurrentPrefabStage().HasSceneBeenModified())) @@ -259,6 +259,15 @@ void AutoSaveButtons() if (EditorGUI.EndChangeCheck()) stage.autoSave = autoSaveForScene; } + + if (!openForEdit) + { + if (GUILayout.Button(Styles.checkoutButtonContent, Styles.button)) + { + Task task = Provider.Checkout(AssetDatabase.LoadAssetAtPath(item.prefabAssetPath), CheckoutMode.Both); + task.Wait(); + } + } } } diff --git a/Editor/Mono/Scripting/Compilers/BooCompiler.cs b/Editor/Mono/Scripting/Compilers/BooCompiler.cs index 91bc774c92..8cbb470b10 100644 --- a/Editor/Mono/Scripting/Compilers/BooCompiler.cs +++ b/Editor/Mono/Scripting/Compilers/BooCompiler.cs @@ -23,19 +23,19 @@ override protected Program StartCompiler() { "-debug", "-target:library", - "-out:" + _island._output, + "-out:" + m_Island._output, "-x-type-inference-rule-attribute:" + typeof(UnityEngineInternal.TypeInferenceRuleAttribute) }; - foreach (string dll in _island._references) + foreach (string dll in m_Island._references) arguments.Add("-r:" + PrepareFileName(dll)); - foreach (string define in _island._defines.Distinct()) + foreach (string define in m_Island._defines.Distinct()) arguments.Add("-define:" + define); - foreach (string source in _island._files) + foreach (string source in m_Island._files) arguments.Add(PrepareFileName(source)); string compilerPath = Path.Combine(GetBooCompilerDirectory(), "booc.exe"); - return StartCompiler(_island._target, compilerPath, arguments, GetBooProfileDirectory()); + return StartCompiler(m_Island._target, compilerPath, arguments, GetBooProfileDirectory()); } protected override CompilerOutputParserBase CreateOutputParser() @@ -43,6 +43,11 @@ protected override CompilerOutputParserBase CreateOutputParser() return new BooCompilerOutputParser(); } + protected override string[] GetSystemReferenceDirectories() + { + return new[] { GetBooCompilerDirectory() }; + } + string GetBooCompilerDirectory() { if (EditorApplication.scriptingRuntimeVersion == ScriptingRuntimeVersion.Legacy) @@ -54,7 +59,7 @@ string GetBooCompilerDirectory() string GetBooProfileDirectory() { if (EditorApplication.scriptingRuntimeVersion == ScriptingRuntimeVersion.Legacy) - return BuildPipeline.CompatibilityProfileToClassLibFolder(_island._api_compatibility_level); + return BuildPipeline.CompatibilityProfileToClassLibFolder(m_Island._api_compatibility_level); return k_UnityScriptProfileDirectory; } diff --git a/Editor/Mono/Scripting/Compilers/CSharpLanguage.cs b/Editor/Mono/Scripting/Compilers/CSharpLanguage.cs index 8d83e543ee..a3481ab119 100644 --- a/Editor/Mono/Scripting/Compilers/CSharpLanguage.cs +++ b/Editor/Mono/Scripting/Compilers/CSharpLanguage.cs @@ -56,45 +56,25 @@ public override bool CompilerRequiresAdditionalReferences() return true; } - public string GetNamespaceNewRuntime(string filePath, string definedSymbols) + static string[] GetSystemReferenceDirectories(ApiCompatibilityLevel apiCompatibilityLevel) { - var definedSymbolSplit = definedSymbols.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - string[] defines = null; - var responseFilePath = Path.Combine("Assets", MonoCSharpCompiler.ReponseFilename); - try - { - var responseFileData = ScriptCompilerBase.ParseResponseFileFromFile(responseFilePath); - defines = new string[responseFileData.Defines.Length + definedSymbolSplit.Length]; - Array.Copy(definedSymbolSplit, defines, definedSymbolSplit.Length); - Array.Copy(responseFileData.Defines, 0, defines, definedSymbolSplit.Length, responseFileData.Defines.Length); - } - catch (Exception e) - { - Debug.LogException(e); - } - - var uniqueSymbols = new HashSet(defines ?? definedSymbolSplit); - return CSharpNamespaceParser.GetNamespace(ReadAndConverteNewLines(filePath).ReadToEnd(), Path.GetFileNameWithoutExtension(filePath), uniqueSymbols.ToArray()); + return MonoLibraryHelpers.GetSystemReferenceDirectories(apiCompatibilityLevel); } - public string GetNamespaceOldRuntime(string filePath, string definedSymbols) + public string GetNamespaceNewRuntime(string filePath, string definedSymbols, string[] defines) { var definedSymbolSplit = definedSymbols.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - string[] defines = null; - var responseFilePath = Path.Combine("Assets", MonoCSharpCompiler.ReponseFilename); - try - { - var responseFileData = ScriptCompilerBase.ParseResponseFileFromFile(responseFilePath); - defines = new string[responseFileData.Defines.Length + definedSymbolSplit.Length]; - Array.Copy(definedSymbolSplit, defines, definedSymbolSplit.Length); - Array.Copy(responseFileData.Defines, 0, defines, definedSymbolSplit.Length, responseFileData.Defines.Length); - } - catch (Exception e) - { - Debug.LogException(e); - } + var uniqueSymbols = defines.Union(definedSymbolSplit).Distinct().ToArray(); + return CSharpNamespaceParser.GetNamespace( + ReadAndConverteNewLines(filePath).ReadToEnd(), + Path.GetFileNameWithoutExtension(filePath), + uniqueSymbols); + } - var uniqueSymbols = new HashSet(defines ?? definedSymbolSplit); + public string GetNamespaceOldRuntime(string filePath, string definedSymbols, string[] defines) + { + var definedSymbolSplit = definedSymbols.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + var uniqueSymbols = defines.Union(definedSymbolSplit).Distinct().ToArray(); using (var parser = ParserFactory.CreateParser(ICSharpCode.NRefactory.SupportedLanguage.CSharp, ReadAndConverteNewLines(filePath))) { foreach (var symbol in uniqueSymbols) @@ -121,13 +101,22 @@ public string GetNamespaceOldRuntime(string filePath, string definedSymbols) public override string GetNamespace(string filePath, string definedSymbols) { + var responseFilePath = Path.Combine("Assets", MonoCSharpCompiler.ResponseFilename); if (EditorApplication.scriptingRuntimeVersion == ScriptingRuntimeVersion.Latest) { - return GetNamespaceNewRuntime(filePath, definedSymbols); + var responseFileData = ScriptCompilerBase.ParseResponseFileFromFile( + responseFilePath, + Application.dataPath, + GetSystemReferenceDirectories(ApiCompatibilityLevel.NET_4_6)); + return GetNamespaceNewRuntime(filePath, definedSymbols, responseFileData.Defines); } else { - return GetNamespaceOldRuntime(filePath, definedSymbols); + var responseFileData = ScriptCompilerBase.ParseResponseFileFromFile( + responseFilePath, + Application.dataPath, + GetSystemReferenceDirectories(ApiCompatibilityLevel.NET_2_0)); + return GetNamespaceOldRuntime(filePath, definedSymbols, responseFileData.Defines); } } diff --git a/Editor/Mono/Scripting/Compilers/MicrosoftCSharpCompiler.cs b/Editor/Mono/Scripting/Compilers/MicrosoftCSharpCompiler.cs index 19a5762ed3..5a19930cbc 100644 --- a/Editor/Mono/Scripting/Compilers/MicrosoftCSharpCompiler.cs +++ b/Editor/Mono/Scripting/Compilers/MicrosoftCSharpCompiler.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using UnityEditor.Modules; +using UnityEditor.Scripting.ScriptCompilation; using UnityEditor.Utils; using UnityEngine; @@ -21,18 +22,18 @@ public MicrosoftCSharpCompiler(MonoIsland island, bool runUpdater) : base(island { } - private BuildTarget BuildTarget { get { return _island._target; } } + BuildTarget BuildTarget => m_Island._target; - private string[] GetClassLibraries() + public static string[] GetClassLibraries(BuildTarget buildTarget) { - var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(BuildTarget); + var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget); if (PlayerSettings.GetScriptingBackend(buildTargetGroup) != ScriptingImplementation.WinRTDotNET) { return new string[] {}; } - if (BuildTarget != BuildTarget.WSAPlayer) - throw new InvalidOperationException(string.Format("MicrosoftCSharpCompiler cannot build for .NET Scripting backend for BuildTarget.{0}.", BuildTarget)); + if (buildTarget != BuildTarget.WSAPlayer) + throw new InvalidOperationException($"MicrosoftCSharpCompiler cannot build for .NET Scripting backend for BuildTarget. {buildTarget}."); var resolver = new NuGetPackageResolver { ProjectLockFile = @"UWP\project.lock.json" }; return resolver.Resolve(); @@ -54,7 +55,7 @@ private void FillCompilerOptions(List arguments, out string argsPrefix) { var compilationExtension = platformSupportModule.CreateCompilationExtension(); - arguments.AddRange(GetClassLibraries().Select(r => "/reference:\"" + r + "\"")); + arguments.AddRange(GetClassLibraries(BuildTarget).Select(r => "/reference:\"" + r + "\"")); arguments.AddRange(compilationExtension.GetAdditionalAssemblyReferences().Select(r => "/reference:\"" + r + "\"")); arguments.AddRange(compilationExtension.GetWindowsMetadataReferences().Select(r => "/reference:\"" + r + "\"")); arguments.AddRange(compilationExtension.GetAdditionalDefines().Select(d => "/define:" + d)); @@ -69,14 +70,14 @@ private static void ThrowCompilerNotFoundException(string path) private Program StartCompilerImpl(List arguments, string argsPrefix) { - foreach (string dll in _island._references) + foreach (string dll in m_Island._references) arguments.Add("/reference:" + PrepareFileName(dll)); - foreach (string define in _island._defines.Distinct()) + foreach (string define in m_Island._defines.Distinct()) arguments.Add("/define:" + define); - var filePathMappings = new List(_island._files.Length); - foreach (var source in _island._files) + var filePathMappings = new List(m_Island._files.Length); + foreach (var source in m_Island._files) { var f = PrepareFileName(source); if (Application.platform == RuntimePlatform.WindowsEditor) @@ -117,7 +118,7 @@ private Program StartCompilerImpl(List arguments, string argsPrefix) if (!AddCustomResponseFileIfPresent(arguments, ReponseFilename) && PlayerSettings.GetScriptingBackend(buildTargetGroup) != ScriptingImplementation.WinRTDotNET) { if (AddCustomResponseFileIfPresent(arguments, "mcs.rsp")) - UnityEngine.Debug.LogWarning(string.Format("Using obsolete custom response file 'mcs.rsp'. Please use '{0}' instead.", ReponseFilename)); + UnityEngine.Debug.LogWarning($"Using obsolete custom response file 'mcs.rsp'. Please use '{ReponseFilename}' instead."); } var responseFile = CommandLineFormatter.GenerateResponseFile(arguments); @@ -168,7 +169,7 @@ static bool UseNetCoreCompiler() protected override Program StartCompiler() { - var outputPath = PrepareFileName(_island._output); + var outputPath = PrepareFileName(m_Island._output); // Always build with "/debug:pdbonly", "/optimize+", because even if the assembly is optimized // it seems you can still succesfully debug C# scripts in Visual Studio @@ -179,11 +180,12 @@ protected override Program StartCompiler() "/out:" + outputPath }; - if (_island._allowUnsafeCode) + if (m_Island._allowUnsafeCode) arguments.Add("/unsafe"); var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(BuildTarget); - if (!_island._development_player) + var disableOptimizations = m_Island._development_player || (m_Island._editor && EditorPrefs.GetBool("AllowAttachedDebuggingOfEditor", true)); + if (!disableOptimizations) { if (PlayerSettings.GetScriptingBackend(buildTargetGroup) == ScriptingImplementation.WinRTDotNET) arguments.Add("/debug:pdbonly"); @@ -205,6 +207,17 @@ protected override Program StartCompiler() return StartCompilerImpl(arguments, argsPrefix); } + protected override string[] GetSystemReferenceDirectories() + { + var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(BuildTarget); + if (BuildTarget == BuildTarget.WSAPlayer && PlayerSettings.GetScriptingBackend(buildTargetGroup) == ScriptingImplementation.WinRTDotNET) + { + return GetClassLibraries(BuildTarget).Select(library => Directory.GetParent(library).FullName).Distinct().ToArray(); + } + + return MonoLibraryHelpers.GetSystemReferenceDirectories(m_Island._api_compatibility_level); + } + protected override string[] GetStreamContainingCompilerMessages() { return GetStandardOutput(); diff --git a/Editor/Mono/Scripting/Compilers/MonoCSharpCompiler.cs b/Editor/Mono/Scripting/Compilers/MonoCSharpCompiler.cs index 6572173376..f442a9f7b4 100644 --- a/Editor/Mono/Scripting/Compilers/MonoCSharpCompiler.cs +++ b/Editor/Mono/Scripting/Compilers/MonoCSharpCompiler.cs @@ -14,13 +14,13 @@ namespace UnityEditor.Scripting.Compilers { class MonoCSharpCompiler : MonoScriptCompilerBase { - public static readonly string ReponseFilename = "mcs.rsp"; + public static readonly string ResponseFilename = "mcs.rsp"; public MonoCSharpCompiler(MonoIsland island, bool runUpdater) : base(island, runUpdater) { } - override protected Program StartCompiler() + protected override Program StartCompiler() { var arguments = new List { @@ -28,23 +28,23 @@ override protected Program StartCompiler() "-target:library", "-nowarn:0169", "-langversion:" + ((EditorApplication.scriptingRuntimeVersion == ScriptingRuntimeVersion.Latest) ? "6" : "4"), - "-out:" + PrepareFileName(_island._output), + "-out:" + PrepareFileName(m_Island._output), "-nostdlib", }; - if (_island._allowUnsafeCode) + if (m_Island._allowUnsafeCode) arguments.Add("-unsafe"); - if (!_island._development_player && !_island._editor) + if (!m_Island._development_player && !m_Island._editor) arguments.Add("-optimize"); - foreach (string dll in _island._references) + foreach (string dll in m_Island._references) arguments.Add("-r:" + PrepareFileName(dll)); - foreach (string define in _island._defines.Distinct()) + foreach (string define in m_Island._defines.Distinct()) arguments.Add("-define:" + define); - var pathMappings = new List(_island._files.Length); - foreach (string source in _island._files) + var pathMappings = new List(m_Island._files.Length); + foreach (string source in m_Island._files) { var preparedFileName = PrepareFileName(source); if (preparedFileName != source) @@ -53,35 +53,39 @@ override protected Program StartCompiler() arguments.Add(preparedFileName); } - if (!AddCustomResponseFileIfPresent(arguments, ReponseFilename)) + if (!AddCustomResponseFileIfPresent(arguments, ResponseFilename)) { - if (_island._api_compatibility_level == ApiCompatibilityLevel.NET_2_0_Subset && AddCustomResponseFileIfPresent(arguments, "smcs.rsp")) - Debug.LogWarning(string.Format("Using obsolete custom response file 'smcs.rsp'. Please use '{0}' instead.", ReponseFilename)); - else if (_island._api_compatibility_level == ApiCompatibilityLevel.NET_2_0 && AddCustomResponseFileIfPresent(arguments, "gmcs.rsp")) - Debug.LogWarning(string.Format("Using obsolete custom response file 'gmcs.rsp'. Please use '{0}' instead.", ReponseFilename)); + if (m_Island._api_compatibility_level == ApiCompatibilityLevel.NET_2_0_Subset + && AddCustomResponseFileIfPresent(arguments, "smcs.rsp")) + { + Debug.LogWarning("Using obsolete custom response file \'smcs.rsp\'. " + + $"Please use '{ResponseFilename}' instead."); + } + else if (m_Island._api_compatibility_level == ApiCompatibilityLevel.NET_2_0 + && AddCustomResponseFileIfPresent(arguments, "gmcs.rsp")) + { + Debug.LogWarning("Using obsolete custom response file \'gmcs.rsp\'. " + + $"Please use '{ResponseFilename}' instead."); + } } return StartCompiler( - _island._target, - GetCompilerPath(arguments), + m_Island._target, + GetCompilerPath(), arguments, - BuildPipeline.CompatibilityProfileToClassLibFolder(_island._api_compatibility_level), + BuildPipeline.CompatibilityProfileToClassLibFolder(m_Island._api_compatibility_level), false, MonoInstallationFinder.GetMonoInstallation(MonoInstallationFinder.MonoBleedingEdgeInstallation), pathMappings ); } - private string GetCompilerPath(List arguments) + static string GetCompilerPath() { string dir = MonoInstallationFinder.GetProfileDirectory("4.5", MonoInstallationFinder.MonoBleedingEdgeInstallation); var compilerPath = Path.Combine(dir, "mcs.exe"); if (File.Exists(compilerPath)) { - var systemAssemblyDirectory = MonoLibraryHelpers.GetSystemReferenceDirectory(_island._api_compatibility_level); - - if (!string.IsNullOrEmpty(systemAssemblyDirectory) && Directory.Exists(systemAssemblyDirectory)) - arguments.Add("-lib:" + PrepareFileName(systemAssemblyDirectory)); return compilerPath; } @@ -93,6 +97,11 @@ protected override CompilerOutputParserBase CreateOutputParser() return new MonoCSharpCompilerOutputParser(); } + protected override string[] GetSystemReferenceDirectories() + { + return MonoLibraryHelpers.GetSystemReferenceDirectories(m_Island._api_compatibility_level); + } + public static string[] Compile(string[] sources, string[] references, string[] defines, string outputFile, bool allowUnsafeCode) { var island = new MonoIsland(BuildTarget.StandaloneWindows, ApiCompatibilityLevel.NET_2_0_Subset, allowUnsafeCode, sources, references, defines, outputFile); diff --git a/Editor/Mono/Scripting/Compilers/MonoScriptCompilerBase.cs b/Editor/Mono/Scripting/Compilers/MonoScriptCompilerBase.cs index 52122f5232..6d919acd25 100644 --- a/Editor/Mono/Scripting/Compilers/MonoScriptCompilerBase.cs +++ b/Editor/Mono/Scripting/Compilers/MonoScriptCompilerBase.cs @@ -16,14 +16,14 @@ protected MonoScriptCompilerBase(MonoIsland island, bool runUpdater) : base(isla protected ManagedProgram StartCompiler(BuildTarget target, string compiler, List arguments) { - return StartCompiler(target, compiler, arguments, BuildPipeline.CompatibilityProfileToClassLibFolder(_island._api_compatibility_level)); + return StartCompiler(target, compiler, arguments, BuildPipeline.CompatibilityProfileToClassLibFolder(m_Island._api_compatibility_level)); } protected ManagedProgram StartCompiler(BuildTarget target, string compiler, List arguments, string profileDirectory) { AddCustomResponseFileIfPresent(arguments, Path.GetFileNameWithoutExtension(compiler) + ".rsp"); - var monoInstallation = (PlayerSettingsEditor.IsLatestApiCompatibility(_island._api_compatibility_level)) + var monoInstallation = PlayerSettingsEditor.IsLatestApiCompatibility(m_Island._api_compatibility_level) ? MonoInstallationFinder.GetMonoBleedingEdgeInstallation() : MonoInstallationFinder.GetMonoInstallation(); return StartCompiler(target, compiler, arguments, profileDirectory, true, monoInstallation); diff --git a/Editor/Mono/Scripting/Compilers/ScriptCompilerBase.cs b/Editor/Mono/Scripting/Compilers/ScriptCompilerBase.cs index fd3072da16..d140999018 100644 --- a/Editor/Mono/Scripting/Compilers/ScriptCompilerBase.cs +++ b/Editor/Mono/Scripting/Compilers/ScriptCompilerBase.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using UnityEditor.Scripting.ScriptCompilation; using UnityEngine; using UnityEditor.Utils; @@ -22,9 +23,10 @@ public class Reference } public string[] Defines; - public Reference[] References; + public Reference[] FullPathReferences; public bool Unsafe; public string[] Errors; + public string[] OtherArguments; } public class CompilerOption @@ -38,9 +40,10 @@ public class CompilerOption private Program process; private string _responseFile = null; private bool _runAPIUpdater; + string m_ProjectDirectory; // ToDo: would be nice to move MonoIsland to MonoScriptCompilerBase - protected MonoIsland _island; + protected MonoIsland m_Island; protected abstract Program StartCompiler(); @@ -48,8 +51,9 @@ public class CompilerOption protected ScriptCompilerBase(MonoIsland island, bool runAPIUpdater) { - _island = island; + m_Island = island; _runAPIUpdater = runAPIUpdater; + m_ProjectDirectory = Directory.GetParent(Application.dataPath).FullName.ConvertSeparatorsToUnity(); } protected string[] GetErrorOutput() @@ -98,15 +102,17 @@ public void WaitForCompilationToFinish() protected string GetMonoProfileLibDirectory() { - var profile = BuildPipeline.CompatibilityProfileToClassLibFolder(_island._api_compatibility_level); + var profile = BuildPipeline.CompatibilityProfileToClassLibFolder(m_Island._api_compatibility_level); - var monoInstall = (PlayerSettingsEditor.IsLatestApiCompatibility(_island._api_compatibility_level)) + var monoInstall = PlayerSettingsEditor.IsLatestApiCompatibility(m_Island._api_compatibility_level) ? MonoInstallationFinder.MonoBleedingEdgeInstallation : MonoInstallationFinder.MonoInstallation; return MonoInstallationFinder.GetProfileDirectory(profile, monoInstall); } + protected abstract string[] GetSystemReferenceDirectories(); + protected bool AddCustomResponseFileIfPresent(List arguments, string responseFileName) { var relativeCustomResponseFilePath = Path.Combine("Assets", responseFileName); @@ -114,19 +120,36 @@ protected bool AddCustomResponseFileIfPresent(List arguments, string res if (!File.Exists(relativeCustomResponseFilePath)) return false; - arguments.Add("@" + relativeCustomResponseFilePath); + var responseFileData = ParseResponseFileFromFile( + Path.Combine(m_ProjectDirectory, relativeCustomResponseFilePath), + Application.dataPath, + GetSystemReferenceDirectories()); + foreach (var error in responseFileData.Errors) + { + Debug.LogError($"{relativeCustomResponseFilePath} Parse Error : {error}"); + } + + arguments.AddRange(responseFileData.Defines.Distinct().Select(define => "/define:" + define)); + arguments.AddRange(responseFileData.FullPathReferences.Select(reference => + "/reference:" + PrepareFileName(reference.Assembly))); + + if (responseFileData.Unsafe) arguments.Add("/unsafe"); + arguments.AddRange(responseFileData.OtherArguments); return true; } - public static ResponseFileData ParseResponseFileFromFile(string responseFilePath) + public static ResponseFileData ParseResponseFileFromFile( + string responseFilePath, + string projectDirectory, + string[] systemReferenceDirectories) { if (!File.Exists(responseFilePath)) { var empty = new ResponseFileData { Defines = new string[0], - References = new ResponseFileData.Reference[0], + FullPathReferences = new ResponseFileData.Reference[0], Unsafe = false, Errors = new string[0] }; @@ -136,7 +159,11 @@ public static ResponseFileData ParseResponseFileFromFile(string responseFilePath var responseFileText = File.ReadAllText(responseFilePath); - return ParseResponseFileText(responseFileText); + return ParseResponseFileText( + responseFileText, + responseFilePath, + projectDirectory, + systemReferenceDirectories); } // From: @@ -204,7 +231,11 @@ static string[] ResponseFileTextToStrings(string responseFileText) return args.ToArray(); } - public static ResponseFileData ParseResponseFileText(string responseFileText) + static ResponseFileData ParseResponseFileText( + string responseFileText, + string responseFileName, + string projectDirectory, + string[] systemReferenceDirectories) { var compilerOptions = new List(); @@ -232,6 +263,7 @@ public static ResponseFileData ParseResponseFileText(string responseFileText) compilerOptions.Add(new CompilerOption { Arg = arg, Value = value }); } + var responseArguments = new List(); var defines = new List(); var references = new List(); bool unsafeDefined = false; @@ -276,24 +308,61 @@ public static ResponseFileData ParseResponseFileText(string responseFileText) break; } - foreach (string reference in refs) + var reference = refs[0]; + if (reference.Length == 0) { - if (reference.Length == 0) - continue; + continue; + } - int index = reference.IndexOf('='); - if (index > -1) - { - string alias = reference.Substring(0, index); - string assembly = reference.Substring(index + 1); + ResponseFileData.Reference responseReference; + + int index = reference.IndexOf('='); + if (index > -1) + { + string alias = reference.Substring(0, index); + string assembly = reference.Substring(index + 1); + + responseReference = new ResponseFileData.Reference { Alias = alias, Assembly = assembly }; + } + else + { + responseReference = new ResponseFileData.Reference { Alias = string.Empty, Assembly = reference }; + } - references.Add(new ResponseFileData.Reference { Alias = alias, Assembly = assembly }); + string fullPathReference = ""; + var referencePath = responseReference.Assembly; + if (Path.IsPathRooted(referencePath)) + { + fullPathReference = referencePath; + } + else + { + foreach (var directory in systemReferenceDirectories) + { + var systemReferencePath = Paths.Combine(directory, referencePath); + if (File.Exists(systemReferencePath)) + { + fullPathReference = systemReferencePath; + break; + } } - else + + var userPath = Paths.Combine(projectDirectory, referencePath); + if (File.Exists(userPath)) { - references.Add(new ResponseFileData.Reference { Alias = string.Empty, Assembly = reference }); + fullPathReference = userPath; } } + + if (fullPathReference == "") + { + errors.Add($"{responseFileName}: not parsed correctly: {responseReference.Assembly} could not be found as a system library.\n" + + "If this was meant as a user reference please provide the relative path from project root (parent of the Assets folder) in the response file."); + continue; + } + + responseReference.Assembly = fullPathReference.Replace('\\', '/'); + references.Add(responseReference); } break; @@ -309,15 +378,20 @@ public static ResponseFileData ParseResponseFileText(string responseFileText) unsafeDefined = false; } break; + default: + var valueWithColon = value.Length == 0 ? "" : ":" + value; + responseArguments.Add(arg + valueWithColon); + break; } } var responseFileData = new ResponseFileData { Defines = defines.ToArray(), - References = references.ToArray(), + FullPathReferences = references.ToArray(), Unsafe = unsafeDefined, - Errors = errors.ToArray() + Errors = errors.ToArray(), + OtherArguments = responseArguments.ToArray(), }; return responseFileData; @@ -336,7 +410,11 @@ public virtual CompilerMessage[] GetCompilerMessages() DumpStreamOutputToLog(); - return CreateOutputParser().Parse(GetStreamContainingCompilerMessages(), CompilationHadFailure(), Path.GetFileName(_island._output)).ToArray(); + return CreateOutputParser().Parse( + GetStreamContainingCompilerMessages(), + CompilationHadFailure(), + Path.GetFileName(m_Island._output) + ).ToArray(); } protected bool CompilationHadFailure() @@ -367,7 +445,11 @@ private void DumpStreamOutputToLog() string[] stdOutput = GetStandardOutput(); - Console.WriteLine("-----CompilerOutput:-stdout--exitcode: " + process.ExitCode + "--compilationhadfailure: " + hadCompilationFailure + "--outfile: " + _island._output); + Console.WriteLine( + "-----CompilerOutput:-stdout--exitcode: " + process.ExitCode + + "--compilationhadfailure: " + hadCompilationFailure + + "--outfile: " + m_Island._output + ); foreach (string line in stdOutput) Console.WriteLine(line); @@ -386,7 +468,11 @@ protected void RunAPIUpdaterIfRequired(string responseFile, IList pathMa var pathMappingsFilePath = Path.GetTempFileName(); File.WriteAllLines(pathMappingsFilePath, pathMappings.ToArray()); - APIUpdaterHelper.UpdateScripts(responseFile, _island.GetExtensionOfSourceFiles(), PrepareFileName(pathMappingsFilePath)); + APIUpdaterHelper.UpdateScripts( + responseFile, + m_Island.GetExtensionOfSourceFiles(), + PrepareFileName(pathMappingsFilePath) + ); } } diff --git a/Editor/Mono/Scripting/Compilers/UWPReferences.cs b/Editor/Mono/Scripting/Compilers/UWPReferences.cs index f057f616bf..d2c2861e13 100644 --- a/Editor/Mono/Scripting/Compilers/UWPReferences.cs +++ b/Editor/Mono/Scripting/Compilers/UWPReferences.cs @@ -31,20 +31,22 @@ internal class UWPSDK { public readonly Version Version; public readonly Version MinVSVersion; + public readonly IEnumerable PreviousSDKs; - public UWPSDK(Version version, Version minVSVersion) + public UWPSDK(Version version, Version minVSVersion, IEnumerable previousSDKs) { Version = version; MinVSVersion = minVSVersion; + PreviousSDKs = previousSDKs; } } - internal class PreviousSDKVersion + internal class PreviousUWPSDK { - public readonly string Version; + public readonly Version Version; public readonly bool DefaultFallback; - public PreviousSDKVersion(string version, bool defaultFallback) + public PreviousUWPSDK(Version version, bool defaultFallback) { Version = version; DefaultFallback = defaultFallback; @@ -71,13 +73,19 @@ public UWPExtension(string manifest, string windowsKitsFolder, string sdkVersion } } - public static string[] GetReferences(Version sdkVersion) + private static readonly Version kMinimumSupportedUWPVersion = new Version(10, 0, 10240, 0); + private static readonly PreviousUWPSDK kMinimumSupportedPreviousUWPSDK = new PreviousUWPSDK(kMinimumSupportedUWPVersion, true); + private static readonly UWPSDK kMinimumSupportedUWPSDK = new UWPSDK(kMinimumSupportedUWPVersion, new Version(14, 0), new[] { kMinimumSupportedPreviousUWPSDK }); + + public static UWPSDK MinimumSupportedUWPSDK { get { return kMinimumSupportedUWPSDK; } } + + public static string[] GetReferences(UWPSDK sdk) { var folder = GetWindowsKit10(); if (string.IsNullOrEmpty(folder)) return new string[0]; - var version = SdkVersionToString(sdkVersion); + var version = SdkVersionToString(sdk.Version); var references = new HashSet(StringComparer.InvariantCultureIgnoreCase); var windowsWinMd = CombinePaths(folder, "UnionMetadata", version, "Facade", "Windows.winmd"); @@ -102,13 +110,13 @@ public static string[] GetReferences(Version sdkVersion) return references.ToArray(); } - public static IEnumerable GetExtensionSDKs(Version sdkVersion) + public static IEnumerable GetExtensionSDKs(UWPSDK sdk) { var windowsKit10Directory = GetWindowsKit10(); if (string.IsNullOrEmpty(windowsKit10Directory)) return new UWPExtensionSDK[0]; - return GetExtensionSDKs(windowsKit10Directory, SdkVersionToString(sdkVersion)); + return GetExtensionSDKs(windowsKit10Directory, SdkVersionToString(sdk.Version)); } static string SdkVersionToString(Version version) @@ -125,17 +133,17 @@ static string SdkVersionToString(Version version) return sdkVersion; } - public static IEnumerable>>> GetInstalledSDKs() + public static IEnumerable GetInstalledSDKs() { var windowsKit10Directory = GetWindowsKit10(); if (string.IsNullOrEmpty(windowsKit10Directory)) - return Enumerable.Empty>>>(); + return Enumerable.Empty(); var platformsUAP = CombinePaths(windowsKit10Directory, "Platforms", "UAP"); if (!Directory.Exists(platformsUAP)) - return Enumerable.Empty>>>(); + return Enumerable.Empty(); - var allSDKs = new Dictionary>>(); + var allSDKs = new List(); var filesUnderPlatformsUAP = Directory.GetFiles(platformsUAP, "*", SearchOption.AllDirectories); var allPlatformXmlFiles = filesUnderPlatformsUAP.Where(f => string.Equals("Platform.xml", Path.GetFileName(f), StringComparison.OrdinalIgnoreCase)); @@ -158,50 +166,64 @@ public static IEnumerable e.Value).FirstOrDefault(); // Get supported previous versionss var previousVersionPath = Path.Combine(Path.GetDirectoryName(platformXmlFile), "PreviousPlatforms.xml"); - var previousVersions = new SortedList(); + var previousVersions = new List(); if (File.Exists(previousVersionPath)) { + XNamespace xn = "http://microsoft.com/schemas/Windows/SDK/PreviousPlatforms"; + XDocument previousPlatformsDocument = null; + try { - XNamespace xn = "http://microsoft.com/schemas/Windows/SDK/PreviousPlatforms"; - var previousPlatformsDocument = XDocument.Load(previousVersionPath); + previousPlatformsDocument = XDocument.Load(previousVersionPath); + } + catch + { + } - foreach (XElement previousPlatformElement in previousPlatformsDocument.Element(xn + "PreviousPlatforms").Elements(xn + "ApplicationPlatform")) + if (previousPlatformsDocument != null) + { + var previousPlatformsElement = previousPlatformsDocument.Element(xn + "PreviousPlatforms"); + if (previousPlatformsElement != null) { - var previousVersion = previousPlatformElement.Attribute("version").Value; - var isDefault = false; - - try + foreach (XElement previousPlatformElement in previousPlatformsElement.Elements(xn + "ApplicationPlatform")) { - isDefault = bool.Parse(previousPlatformElement.Attribute("IsDefaultFallback").Value); + var versionAttribute = previousPlatformElement.Attribute("version"); + if (versionAttribute != null) + { + var previousVersionString = versionAttribute.Value; + bool isDefault = false; + + var isDefaultFallbackAttribute = previousPlatformElement.Attribute("IsDefaultFallback"); + if (isDefaultFallbackAttribute != null) + bool.TryParse(isDefaultFallbackAttribute.Value, out isDefault); + + var previousVersion = TryParseVersion(previousVersionString); + if (previousVersion != null && previousVersion >= kMinimumSupportedUWPVersion) + previousVersions.Add(new PreviousUWPSDK(previousVersion, isDefault)); + } } - catch - { - // Ignore exception. The IsDefaultFallback attribute isn't present. This is OK. - } - - previousVersions.Add(previousVersion, new PreviousSDKVersion(previousVersion, isDefault)); } } - catch - { - // Ignore exception. We'll just use the default below. - } } if (previousVersions.Count == 0) { - // For previous versions, only support the current version if no PreviousVersions.xml was found. - previousVersions.Add(version.ToString(), new PreviousSDKVersion(version.ToString(), true)); + // For previous versions, only support the current version and our minimum supported version if no PreviousVersions.xml was found. + previousVersions.Add(new PreviousUWPSDK(version, true)); + + if (version > kMinimumSupportedUWPVersion) + previousVersions.Add(new PreviousUWPSDK(kMinimumSupportedUWPVersion, false)); } - previousVersions.Reverse(); - allSDKs.Add(new UWPSDK(version, TryParseVersion(minVSVersionString)), previousVersions); + allSDKs.Add(new UWPSDK(version, TryParseVersion(minVSVersionString), previousVersions)); } } } diff --git a/Editor/Mono/Scripting/Compilers/UnityScriptCompiler.cs b/Editor/Mono/Scripting/Compilers/UnityScriptCompiler.cs index 48b4ab118e..7b4c4c5809 100644 --- a/Editor/Mono/Scripting/Compilers/UnityScriptCompiler.cs +++ b/Editor/Mono/Scripting/Compilers/UnityScriptCompiler.cs @@ -25,7 +25,7 @@ protected override CompilerOutputParserBase CreateOutputParser() return new UnityScriptCompilerOutputParser(); } - override protected Program StartCompiler() + protected override Program StartCompiler() { var arguments = new List { @@ -37,30 +37,30 @@ override protected Program StartCompiler() "-nowarn:BCW0016", "-nowarn:BCW0003", "-method:Main", - "-out:" + _island._output, + "-out:" + m_Island._output, "-x-type-inference-rule-attribute:" + typeof(UnityEngineInternal.TypeInferenceRuleAttribute) }; if (StrictBuildTarget()) arguments.Add("-pragmas:strict,downcast"); - foreach (var define in _island._defines.Distinct()) + foreach (var define in m_Island._defines.Distinct()) arguments.Add("-define:" + define); - foreach (var dll in _island._references) + foreach (var dll in m_Island._references) arguments.Add("-r:" + PrepareFileName(dll)); - var compilingEditorScripts = Array.Exists(_island._references, UnityEditorPattern.IsMatch); + var compilingEditorScripts = Array.Exists(m_Island._references, UnityEditorPattern.IsMatch); if (compilingEditorScripts) arguments.Add("-i:UnityEditor"); - else if (!BuildPipeline.IsUnityScriptEvalSupported(_island._target)) - arguments.Add(string.Format("-disable-eval:eval is not supported on the current build target ({0}).", _island._target)); + else if (!BuildPipeline.IsUnityScriptEvalSupported(m_Island._target)) + arguments.Add($"-disable-eval:eval is not supported on the current build target ({m_Island._target})."); - foreach (string source in _island._files) + foreach (string source in m_Island._files) arguments.Add(PrepareFileName(source)); var compilerPath = Path.Combine(GetUnityScriptCompilerDirectory(), "us.exe"); - return StartCompiler(_island._target, compilerPath, arguments, GetUnityScriptProfileDirectory()); + return StartCompiler(m_Island._target, compilerPath, arguments, GetUnityScriptProfileDirectory()); } string GetUnityScriptCompilerDirectory() @@ -74,14 +74,19 @@ string GetUnityScriptCompilerDirectory() string GetUnityScriptProfileDirectory() { if (EditorApplication.scriptingRuntimeVersion == ScriptingRuntimeVersion.Legacy) - return BuildPipeline.CompatibilityProfileToClassLibFolder(_island._api_compatibility_level); + return BuildPipeline.CompatibilityProfileToClassLibFolder(m_Island._api_compatibility_level); return k_UnityScriptProfileDirectory; } private bool StrictBuildTarget() { - return Array.IndexOf(_island._defines, "ENABLE_DUCK_TYPING") == -1; + return Array.IndexOf(m_Island._defines, "ENABLE_DUCK_TYPING") == -1; + } + + protected override string[] GetSystemReferenceDirectories() + { + return new[] { GetUnityScriptCompilerDirectory() }; } protected override string[] GetStreamContainingCompilerMessages() diff --git a/Editor/Mono/Scripting/ScriptCompilation/CSharpNamespaceParser.cs b/Editor/Mono/Scripting/ScriptCompilation/CSharpNamespaceParser.cs index 55e0c6d088..a8a63432fa 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/CSharpNamespaceParser.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/CSharpNamespaceParser.cs @@ -11,6 +11,14 @@ namespace UnityEditor.Scripting.ScriptCompilation { + internal class IllegalNamespaceParsing : Exception + { + public IllegalNamespaceParsing(string className, Exception cause) + : base($"Searching for classname: '{className}' caused error in CSharpNameParser", cause) + { + } + } + internal static class CSharpNamespaceParser { static readonly Regex k_ReDefineExpr = new Regex(@"r'\s+|([=!]=)\s*(true|false)|([_a-zA-Z][_a-zA-Z0-9]*)|([()!]|&&|\|\|)", RegexOptions.Compiled); @@ -20,6 +28,7 @@ internal static class CSharpNamespaceParser static readonly Regex k_VerbatimStrings = new Regex(@"@(""[^""]*"")+", RegexOptions.Compiled); static readonly Regex k_NewlineRegex = new Regex("\r\n?", RegexOptions.Compiled); static readonly Regex k_SingleQuote = new Regex(@"((? defines) var split = source.Split(new[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); foreach (var s in split) { - if (s.Contains("#define")) + var match = k_ConditionalCompilation.Match(s); + var directive = match.Groups[1].Value; + var arg = match.Groups[2].Value; + if (directive == "define") { - if (stack.Count == 0 || stack.Peek().Item1) defines.Add(s.Split(new[] { "#define" }, StringSplitOptions.None)[1].Trim()); + if (stack.Count == 0 || stack.Peek().Item1) defines.Add(arg.Trim()); } - else if (s.Contains("#undefine")) + else if (directive == "undefine") { - if (stack.Count == 0 || stack.Peek().Item1) defines.Add(s.Split(new[] { "#define" }, StringSplitOptions.None)[1].Trim()); + if (stack.Count == 0 || stack.Peek().Item1) defines.Add(arg.Trim()); } - else if (s.Contains("#if")) + else if (directive == "if") { - var evalResult = EvaluateDefine(s.Split(new[] { "#if" }, StringSplitOptions.None)[1].Trim(), defines); + var evalResult = EvaluateDefine(arg.Trim(), defines); var isEmitting = stack.Count == 0 || stack.Peek().Item1; stack.Push(new Tuple(isEmitting && evalResult, isEmitting && !evalResult)); } - else if (s.Contains("#elif")) + else if (directive == "elif") { + var evalResult = EvaluateDefine(arg, defines); var elseEmitting = stack.Peek().Item2; - var evalResult = EvaluateDefine(s.Split(new[] { "#elif" }, StringSplitOptions.None)[1], defines); - stack.Pop(); - stack.Push(new Tuple(elseEmitting && evalResult, elseEmitting && !evalResult)); + stack.Pop(); stack.Push(new Tuple(elseEmitting && evalResult, elseEmitting && !evalResult)); } - else if (s.Contains("#else")) + else if (directive == "else") { var elseEmitting = stack.Peek().Item2; - stack.Pop(); - stack.Push(new Tuple(elseEmitting, false)); + stack.Pop(); stack.Push(new Tuple(elseEmitting, false)); } - else if (s.Contains("#endif")) + else if (directive == "endif") { stack.Pop(); } diff --git a/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs b/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs index f784520a09..1eccdccda8 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs @@ -769,6 +769,17 @@ static bool IsCompatibleWithPlatformAndDefines(TargetAssembly assembly, ScriptAs return assembly.IsCompatibleFunc == null || assembly.IsCompatibleFunc(settings, assembly.Defines); } + public static bool IsCompatibleWithPlatformAndDefines(TargetAssembly assembly, BuildTarget buildTarget, EditorScriptCompilationOptions options) + { + var settings = new ScriptAssemblySettings + { + BuildTarget = buildTarget, + CompilationOptions = options + }; + + return IsCompatibleWithPlatformAndDefines(assembly, settings); + } + internal static TargetAssembly[] CreatePredefinedTargetAssemblies() { var runtimeFirstPassAssemblies = new List(); diff --git a/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs b/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs index b607729175..af0ba707ea 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs @@ -148,7 +148,9 @@ public override void PostprocessMessage(ref CompilerMessage message) var index = message.message.IndexOf(EditorApplication.scriptingRuntimeVersion == ScriptingRuntimeVersion.Latest ? "Consider adding a reference to that assembly." : "Consider adding a reference to assembly"); if (index != -1) message.message = message.message.Substring(0, index); - var moduleName = GetNiceDisplayNameForModule(match.Groups[1].Value); + var moduleName = match.Groups[1].Value; + moduleName = ModuleMetadata.GetExcludingModule(moduleName); + moduleName = GetNiceDisplayNameForModule(moduleName); message.message += string.Format("Enable the built in package '{0}' in the Package Manager window to fix this error.", moduleName); } @@ -163,6 +165,7 @@ public override void PostprocessMessage(ref CompilerMessage message) HashSet dirtyTargetAssemblies = new HashSet(); HashSet dirtyPrecompiledAssemblies = new HashSet(); HashSet runScriptUpdaterAssemblies = new HashSet(); + bool recompileAllScriptsOnNextTick; PrecompiledAssembly[] precompiledAssemblies; CustomScriptAssembly[] customScriptAssemblies = new CustomScriptAssembly[0]; EditorBuildRules.TargetAssembly[] customTargetAssemblies; // TargetAssemblies for customScriptAssemblies. @@ -238,6 +241,23 @@ public void DirtyScript(string path) dirtyScripts.Add(path); } + public void DirtyMovedScript(string oldPath, string newPath) + { + DirtyScript(newPath); + + var targetAssembly = EditorBuildRules.GetTargetAssembly(oldPath, projectDirectory, customTargetAssemblies); + + // The target assembly might not exist any more. + if (targetAssembly == null) + { + areAllScriptsDirty = true; + } + else + { + dirtyTargetAssemblies.Add(targetAssembly); + } + } + public void DirtyRemovedScript(string path) { allScripts.Remove(path); @@ -280,6 +300,16 @@ public void DirtyPrecompiledAssembly(string path) } } + public void RecompileAllScriptsOnNextTick() + { + recompileAllScriptsOnNextTick = true; + } + + public bool WillRecompileAllScriptsOnNextTick() + { + return recompileAllScriptsOnNextTick; + } + public void ClearDirtyScripts() { dirtyScripts.Clear(); @@ -352,7 +382,7 @@ public PrecompiledAssembly[] GetAllPrecompiledAssemblies() return this.precompiledAssemblies; } - public TargetAssemblyInfo[] GetAllCompiledAndResolvedCustomTargetAssemblies(out CustomScriptAssemblyAndReference[] assembliesWithMissingReference) + public TargetAssemblyInfo[] GetAllCompiledAndResolvedCustomTargetAssemblies(EditorScriptCompilationOptions options, BuildTarget buildTarget, out CustomScriptAssemblyAndReference[] assembliesWithMissingReference) { if (customTargetAssemblies == null) { @@ -391,6 +421,11 @@ public TargetAssemblyInfo[] GetAllCompiledAndResolvedCustomTargetAssemblies(out // of compiled assemblies. foreach (var reference in assembly.References) { + // Don't check references that are not compatible with the current build target, + // as those assemblies have not been compiled. + if (!EditorBuildRules.IsCompatibleWithPlatformAndDefines(reference, buildTarget, options)) + continue; + if (!customTargetAssemblyCompiledPaths.ContainsKey(reference)) { customTargetAssemblyCompiledPaths.Remove(assembly); @@ -487,7 +522,8 @@ void CheckCyclicAssemblyReferences() } } - Exception[] UpdateCustomTargetAssemblies() + public static Exception[] UpdateCustomScriptAssemblies(CustomScriptAssembly[] customScriptAssemblies, + PackageAssembly[] packageAssemblies) { var exceptions = new List(); @@ -495,14 +531,14 @@ Exception[] UpdateCustomTargetAssemblies() { try { - if (m_PackageAssemblies != null && !assembly.PackageAssembly.HasValue) + if (packageAssemblies != null && !assembly.PackageAssembly.HasValue) { var pathPrefix = assembly.PathPrefix.ToLowerInvariant(); - foreach (var packageAssembly in m_PackageAssemblies) + foreach (var packageAssembly in packageAssemblies) { - var lower = AssetPath.ReplaceSeparators(packageAssembly.DirectoryPath).ToLowerInvariant(); - if (pathPrefix.StartsWith(lower)) + var lower = AssetPath.ReplaceSeparators(packageAssembly.DirectoryPath + AssetPath.Separator).ToLowerInvariant(); + if (pathPrefix.StartsWith(lower, StringComparison.Ordinal)) { assembly.PackageAssembly = packageAssembly; break; @@ -521,11 +557,22 @@ Exception[] UpdateCustomTargetAssemblies() } catch (Exception e) { - SetCompilationSetupErrorFlags(CompilationSetupErrorFlags.loadError); exceptions.Add(e); } } + return exceptions.ToArray(); + } + + Exception[] UpdateCustomTargetAssemblies() + { + var exceptions = UpdateCustomScriptAssemblies(customScriptAssemblies, m_PackageAssemblies); + + if (exceptions.Length > 0) + { + SetCompilationSetupErrorFlags(CompilationSetupErrorFlags.loadError); + } + customTargetAssemblies = EditorBuildRules.CreateTargetAssemblies(customScriptAssemblies, precompiledAssemblies); ClearCompilationSetupErrorFlags(CompilationSetupErrorFlags.cyclicReferences); @@ -533,7 +580,7 @@ Exception[] UpdateCustomTargetAssemblies() // customTargetAssemblies being updated. UpdateDirtyTargetAssemblies(); - return exceptions.ToArray(); + return exceptions; } void UpdateDirtyTargetAssemblies() @@ -1175,7 +1222,8 @@ public bool IsCompilationPending() // then compilation will trigger on next TickCompilationPipeline. return DoesProjectFolderHaveAnyDirtyScripts() || ArePrecompiledAssembliesDirty() || - runScriptUpdaterAssemblies.Count() > 0; + runScriptUpdaterAssemblies.Count() > 0 || + recompileAllScriptsOnNextTick; } public bool IsAnyAssemblyBuilderCompiling() @@ -1254,6 +1302,12 @@ public CompileStatus TickCompilationPipeline(EditorScriptCompilationOptions opti if (IsAnyAssemblyBuilderCompiling()) return CompileStatus.Compiling; + if (recompileAllScriptsOnNextTick) + { + DirtyAllScripts(); + recompileAllScriptsOnNextTick = false; + } + // If we are not currently compiling and there are dirty scripts, start compilation. if (!IsCompilationTaskCompiling() && IsCompilationPending()) { diff --git a/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs b/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs index c30ab10805..7ed7b46dfe 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs @@ -155,6 +155,12 @@ public static void DirtyRemovedScript(string path) Instance.DirtyRemovedScript(path); } + [RequiredByNativeCode] + public static void DirtyMovedScript(string oldPath, string newPath) + { + Instance.DirtyMovedScript(oldPath, newPath); + } + [RequiredByNativeCode] public static void DirtyPrecompiledAssembly(string path) { @@ -167,6 +173,18 @@ public static void ClearDirtyScripts() Instance.ClearDirtyScripts(); } + [RequiredByNativeCode] + public static void RecompileAllScriptsOnNextTick() + { + Instance.RecompileAllScriptsOnNextTick(); + } + + [RequiredByNativeCode] + public static bool WillRecompileAllScriptsOnNextTick() + { + return Instance.WillRecompileAllScriptsOnNextTick(); + } + [RequiredByNativeCode] public static void RunScriptUpdaterOnAssembly(string assemblyFilename) { @@ -210,11 +228,11 @@ public static void SetAllPackageAssemblies(EditorCompilation.PackageAssembly[] p } [RequiredByNativeCode] - public static EditorCompilation.TargetAssemblyInfo[] GetAllCompiledAndResolvedCustomTargetAssemblies() + public static EditorCompilation.TargetAssemblyInfo[] GetAllCompiledAndResolvedCustomTargetAssemblies(EditorScriptCompilationOptions options, BuildTarget buildTarget) { EditorCompilation.CustomScriptAssemblyAndReference[] assembliesWithMissingReference = null; - var result = EmitExceptionAsError(() => Instance.GetAllCompiledAndResolvedCustomTargetAssemblies(out assembliesWithMissingReference), new EditorCompilation.TargetAssemblyInfo[0]); + var result = EmitExceptionAsError(() => Instance.GetAllCompiledAndResolvedCustomTargetAssemblies(options, buildTarget, out assembliesWithMissingReference), new EditorCompilation.TargetAssemblyInfo[0]); if (assembliesWithMissingReference.Length > 0) { diff --git a/Editor/Mono/Scripting/ScriptCompilation/EditorScriptCompilationOptions.cs b/Editor/Mono/Scripting/ScriptCompilation/EditorScriptCompilationOptions.cs index 54d00b3510..c2cc1d0136 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/EditorScriptCompilationOptions.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/EditorScriptCompilationOptions.cs @@ -17,6 +17,7 @@ enum EditorScriptCompilationOptions BuildingForIl2Cpp = 1 << 3, BuildingWithAsserts = 1 << 4, BuildingIncludingTestAssemblies = 1 << 5, - BuildingPredefinedAssembliesAllowUnsafeCode = (1 << 6) + BuildingPredefinedAssembliesAllowUnsafeCode = (1 << 6), + BuildingForHeadlessPlayer = 1 << 7 }; } diff --git a/Editor/Mono/Scripting/ScriptCompilation/MonoLibraryHelpers.cs b/Editor/Mono/Scripting/ScriptCompilation/MonoLibraryHelpers.cs index f5578f113f..ecf78af84d 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/MonoLibraryHelpers.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/MonoLibraryHelpers.cs @@ -33,6 +33,16 @@ public static string[] GetSystemLibraryReferences(ApiCompatibilityLevel apiCompa return GetCachedSystemLibraryReferences(apiCompatibilityLevel); } + static string[] FindReferencesInDirectories(this string[] references, string[] directories) + { + return ( + from reference in references + from directory in directories + where File.Exists(Path.Combine(directory, reference)) + select Path.Combine(directory, reference) + ).ToArray(); + } + static string[] GetCachedSystemLibraryReferences(ApiCompatibilityLevel apiCompatibilityLevel) { // We cache the references because they are computed by getting files in directories on disk, @@ -43,7 +53,7 @@ static string[] GetCachedSystemLibraryReferences(ApiCompatibilityLevel apiCompat } var references = new List(); - var monoAssemblyDirectory = GetSystemReferenceDirectory(apiCompatibilityLevel); + var monoAssemblyDirectories = GetSystemReferenceDirectories(apiCompatibilityLevel); if (apiCompatibilityLevel == ApiCompatibilityLevel.NET_Standard_2_0) { @@ -51,19 +61,20 @@ static string[] GetCachedSystemLibraryReferences(ApiCompatibilityLevel apiCompat } else if (apiCompatibilityLevel == ApiCompatibilityLevel.NET_4_6) { - references.AddRange(GetSystemReferences().Select(dll => Path.Combine(monoAssemblyDirectory, dll))); - references.AddRange(GetNet46SystemReferences().Select(dll => Path.Combine(monoAssemblyDirectory, dll))); + references.AddRange(GetSystemReferences().FindReferencesInDirectories(monoAssemblyDirectories)); + references.AddRange(GetNet46SystemReferences().FindReferencesInDirectories(monoAssemblyDirectories)); // Look in the mono assembly directory for a facade folder and get a list of all the DLL's to be // used later by the language compilers. + var monoAssemblyDirectory = MonoInstallationFinder.GetProfileDirectory("4.7.1-api", MonoInstallationFinder.MonoBleedingEdgeInstallation); references.AddRange(Directory.GetFiles(Path.Combine(monoAssemblyDirectory, "Facades"), "*.dll")); references.AddRange(GetBooAndUsReferences().Select(dll => Path.Combine(MonoInstallationFinder.GetProfileDirectory("unityscript", MonoInstallationFinder.MonoBleedingEdgeInstallation), dll))); } else { - references.AddRange(GetSystemReferences().Select(dll => Path.Combine(monoAssemblyDirectory, dll))); - references.AddRange(GetBooAndUsReferences().Select(dll => Path.Combine(monoAssemblyDirectory, dll))); + references.AddRange(GetSystemReferences().FindReferencesInDirectories(monoAssemblyDirectories)); + references.AddRange(GetBooAndUsReferences().FindReferencesInDirectories(monoAssemblyDirectories)); } cachedReferences = new CachedReferences @@ -76,18 +87,40 @@ static string[] GetCachedSystemLibraryReferences(ApiCompatibilityLevel apiCompat return cachedReferences.References; } - public static string GetSystemReferenceDirectory(ApiCompatibilityLevel apiCompatibilityLevel) + static string GetSystemReference(ApiCompatibilityLevel apiCompatibilityLevel) { - if (apiCompatibilityLevel == ApiCompatibilityLevel.NET_Standard_2_0) - return ""; - else if (apiCompatibilityLevel == ApiCompatibilityLevel.NET_4_6) + if (apiCompatibilityLevel == ApiCompatibilityLevel.NET_4_6) return MonoInstallationFinder.GetProfileDirectory("4.7.1-api", MonoInstallationFinder.MonoBleedingEdgeInstallation); - else if (apiCompatibilityLevel == ApiCompatibilityLevel.NET_2_0) + if (apiCompatibilityLevel == ApiCompatibilityLevel.NET_2_0) return MonoInstallationFinder.GetProfileDirectory("2.0-api", MonoInstallationFinder.MonoBleedingEdgeInstallation); return MonoInstallationFinder.GetProfileDirectory(BuildPipeline.CompatibilityProfileToClassLibFolder(apiCompatibilityLevel), MonoInstallationFinder.MonoBleedingEdgeInstallation); } + public static string[] GetSystemReferenceDirectories(ApiCompatibilityLevel apiCompatibilityLevel) + { + if (apiCompatibilityLevel == ApiCompatibilityLevel.NET_Standard_2_0) + { + var systemReferenceDirectories = new List(); + systemReferenceDirectories.Add(NetStandardFinder.GetReferenceDirectory()); + systemReferenceDirectories.Add(NetStandardFinder.GetNetStandardCompatShimsDirectory()); + systemReferenceDirectories.Add(NetStandardFinder.GetNetStandardExtensionsDirectory()); + systemReferenceDirectories.Add(NetStandardFinder.GetDotNetFrameworkCompatShimsDirectory()); + return systemReferenceDirectories.ToArray(); + } + + if (apiCompatibilityLevel == ApiCompatibilityLevel.NET_4_6) + { + var systemReferenceDirectories = new List(); + var frameworkDirectory = GetSystemReference(apiCompatibilityLevel); + systemReferenceDirectories.Add(frameworkDirectory); + systemReferenceDirectories.Add(Path.Combine(frameworkDirectory, "Facades")); + return systemReferenceDirectories.ToArray(); + } + + return new[] { GetSystemReference(apiCompatibilityLevel) }; + } + static string[] GetNetStandardClassLibraries() { var classLibraries = new List(); @@ -125,7 +158,10 @@ static string[] GetNet46SystemReferences() return new[] { "System.Numerics.dll", - "System.Numerics.Vectors.dll" + "System.Numerics.Vectors.dll", + "System.Net.Http.dll", + "Microsoft.CSharp.dll", + "System.Data.dll", }; } diff --git a/Editor/Mono/Settings.cs b/Editor/Mono/Settings.cs index 3c1b5e3b5a..802ebf111e 100644 --- a/Editor/Mono/Settings.cs +++ b/Editor/Mono/Settings.cs @@ -235,25 +235,28 @@ public bool activated } } - internal static bool TryParseUniquePrefString(string prefString, out string name, out Event keyboardEvent) + internal static bool TryParseUniquePrefString(string prefString, out string name, out Event keyboardEvent, out string shortcut) { int i = prefString.IndexOf(";"); if (i < 0) { name = null; keyboardEvent = null; + shortcut = null; return false; } name = prefString.Substring(0, i); - keyboardEvent = Event.KeyboardEvent(prefString.Substring(i + 1)); + shortcut = prefString.Substring(i + 1); + keyboardEvent = Event.KeyboardEvent(shortcut); return true; } public void FromUniqueString(string s) { Load(); - if (!TryParseUniquePrefString(s, out m_name, out m_event)) + if (!TryParseUniquePrefString(s, out m_name, out m_event, out m_Shortcut)) Debug.LogError("Malformed string in Keyboard preferences"); + m_DefaultShortcut = m_Shortcut; } internal void ResetToDefault() diff --git a/Editor/Mono/Settings/Providers/AssetSettingsProvider.cs b/Editor/Mono/Settings/Providers/AssetSettingsProvider.cs index c00bc409b7..c1c276d9e1 100644 --- a/Editor/Mono/Settings/Providers/AssetSettingsProvider.cs +++ b/Editor/Mono/Settings/Providers/AssetSettingsProvider.cs @@ -8,6 +8,7 @@ using UnityEngine.Experimental.UIElements; using UnityEngine.Internal; using UnityEditor.StyleSheets; +using UnityEditorInternal; namespace UnityEditor { @@ -68,9 +69,20 @@ public override void OnDeactivate() public override void OnGUI(string searchContext) { if (m_SettingsEditor != null) + { using (new SettingsWindow.GUIScope()) m_SettingsEditor.OnInspectorGUI(); + // Emulate the Inspector by handling DnD at the native level. + var remainingRect = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.ExpandHeight(true)); + if ((Event.current.type == EventType.DragUpdated || Event.current.type == EventType.DragPerform) && remainingRect.Contains(Event.current.mousePosition)) + { + DragAndDrop.visualMode = InternalEditorUtility.InspectorWindowDrag(new[] { m_SettingsEditor.target }, Event.current.type == EventType.DragPerform); + if (Event.current.type == EventType.DragPerform) + DragAndDrop.AcceptDrag(); + } + } + base.OnGUI(searchContext); } @@ -90,5 +102,11 @@ public override void OnTitleBarGUI() } } } + + public override void OnFooterBarGUI() + { + if (m_SettingsEditor != null) + InspectorWindow.DrawVCSShortInfo(settingsWindow, m_SettingsEditor); + } } } diff --git a/Editor/Mono/Settings/SettingsProvider.cs b/Editor/Mono/Settings/SettingsProvider.cs index 622bdcdb43..c41f58ca89 100644 --- a/Editor/Mono/Settings/SettingsProvider.cs +++ b/Editor/Mono/Settings/SettingsProvider.cs @@ -69,6 +69,7 @@ public string label public Action guiHandler { get; set; } public Action titleBarGuiHandler { get; set; } + public Action footerBarGuiHandler { get; set; } public Action activateHandler { get; set; } public Action deactivateHandler { get; set; } public Func hasSearchInterestHandler { get; set; } @@ -119,6 +120,11 @@ public virtual void OnTitleBarGUI() titleBarGuiHandler?.Invoke(); } + public virtual void OnFooterBarGUI() + { + footerBarGuiHandler?.Invoke(); + } + public void PopulateSearchKeywordsFromGUIContentProperties() { GetSearchKeywordsFromGUIContentProperties(keywords); diff --git a/Editor/Mono/Settings/SettingsService.cs b/Editor/Mono/Settings/SettingsService.cs index 61c625bdeb..2e50c92f30 100644 --- a/Editor/Mono/Settings/SettingsService.cs +++ b/Editor/Mono/Settings/SettingsService.cs @@ -6,14 +6,21 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text; using UnityEngine; using UnityEngine.Internal; namespace UnityEditor { [ExcludeFromDocs] + [InitializeOnLoad] public class SettingsService { + const string k_ProjectSettings = "Edit/Project Settings"; + static SettingsService() + { + } + public static event Action settingsProviderChanged; public static SettingsProvider[] FetchSettingsProviders() @@ -21,7 +28,7 @@ public static SettingsProvider[] FetchSettingsProviders() return FetchSettingProviderFromAttribute() .Concat(FetchSettingProvidersFromAttribute()) - .Concat(FetchPreferenceItems()) + .Concat(FetchDeprecatedPreferenceItems()) .Where(provider => provider != null) .ToArray(); } @@ -31,7 +38,7 @@ public static void NotifySettingsProviderChanged() settingsProviderChanged?.Invoke(); } - private static IEnumerable FetchPreferenceItems() + private static IEnumerable FetchDeprecatedPreferenceItems() { var methods = AttributeHelper.GetMethodsWithAttribute(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); return methods.methodsWithAttributes.Select(method => @@ -39,7 +46,6 @@ private static IEnumerable FetchPreferenceItems() var callback = Delegate.CreateDelegate(typeof(Action), method.info) as Action; if (callback != null) { - Debug.LogWarning("PreferenceItem is deprecated. Use [SettingsProvider] instead"); var attributeName = (method.attribute as PreferenceItem).name; return new SettingsProvider("Preferences/" + attributeName) { guiHandler = searchContext => callback(), scopes = SettingsScopes.User }; } diff --git a/Editor/Mono/Settings/SettingsWindow.cs b/Editor/Mono/Settings/SettingsWindow.cs index e69757227e..6bc3ce7cfa 100644 --- a/Editor/Mono/Settings/SettingsWindow.cs +++ b/Editor/Mono/Settings/SettingsWindow.cs @@ -275,19 +275,22 @@ private void DrawToolbar() private void DrawSettingsPanel() { + if (m_TreeView.currentProvider == null) + return; + + DrawTitleBar(); + using (var scrollViewScope = new EditorGUILayout.ScrollViewScope(m_PosRight, GUILayout.ExpandWidth(true))) { m_PosRight = scrollViewScope.scrollPosition; DrawControls(); } + + DrawFooterBar(); } private void DrawControls() { - if (m_TreeView.currentProvider == null) - return; - - DrawTitleBar(); using (new EditorGUI.LabelHighlightScope(m_SearchText, Styles.settingsPanel.GetColor("-unity-search-highlight-selection-color"), Styles.settingsPanel.GetColor("-unity-search-highlight-color"))) m_TreeView.currentProvider.OnGUI(m_SearchText); } @@ -303,6 +306,11 @@ private void DrawTitleBar() GUILayout.EndHorizontal(); } + private void DrawFooterBar() + { + m_TreeView.currentProvider.OnFooterBarGUI(); + } + private void DrawTreeView() { var splitterRect = m_Splitter.GetSplitterRect(m_Splitter.Children().First()); @@ -331,7 +339,7 @@ internal static void OpenAllSettings() OpenAllSettings(null); } - [MenuItem("Edit/Settings", false, 259, false)] + [MenuItem("Edit/Project Settings...", false, 259, false)] internal static void OpenProjectSettings() { OpenProjectSettings(null); diff --git a/Editor/Mono/ShaderUtil.bindings.cs b/Editor/Mono/ShaderUtil.bindings.cs index cc93b82a05..500172a595 100644 --- a/Editor/Mono/ShaderUtil.bindings.cs +++ b/Editor/Mono/ShaderUtil.bindings.cs @@ -157,6 +157,8 @@ public static bool IsShaderPropertyNonModifiableTexureProperty(Shader s, int pro extern internal static int GetTextureBindingIndex(Shader s, int texturePropertyID); extern internal static int GetTextureSamplerBindingIndex(Shader s, int texturePropertyID); extern internal static int GetLOD(Shader s); + extern internal static int GetSRPBatcherCompatibilityCode(Shader s, int subShaderIdx); + extern internal static string GetSRPBatcherCompatibilityIssueReason(Shader s, int subShaderIdx, int err); extern internal static ulong GetVariantCount(Shader s, bool usedBySceneOnly); extern internal static int GetComputeShaderPlatformCount(ComputeShader s); diff --git a/Editor/Mono/SpriteEditor/SpriteEditorWindow.cs b/Editor/Mono/SpriteEditor/SpriteEditorWindow.cs index e62e1ea2e5..d507ba77b3 100644 --- a/Editor/Mono/SpriteEditor/SpriteEditorWindow.cs +++ b/Editor/Mono/SpriteEditor/SpriteEditorWindow.cs @@ -103,8 +103,8 @@ private void OnFocus() public void RefreshPropertiesCache() { - m_SpriteDataProvider = AssetImporter.GetAtPath(m_SelectedAssetPath) as ISpriteEditorDataProvider; - if (m_SpriteDataProvider == null) + m_SpriteDataProvider = AssetImporter.GetAtPath(m_SelectedAssetPath) as ISpriteEditorDataProvider; + if (!IsSpriteDataProviderValid()) { m_SelectedAssetPath = ""; return; @@ -156,7 +156,7 @@ private Rect warningMessageRect public SpriteImportMode spriteImportMode { - get { return m_SpriteDataProvider == null ? SpriteImportMode.None : m_SpriteDataProvider.spriteImportMode; } + get { return !IsSpriteDataProviderValid() ? SpriteImportMode.None : m_SpriteDataProvider.spriteImportMode; } } bool activeDataProviderSelected @@ -352,7 +352,7 @@ void OnEditorApplicationQuit() void HandleApplyRevertDialog(string dialogTitle, string dialogContent) { - if (textureIsDirty && m_SpriteDataProvider != null) + if (textureIsDirty && IsSpriteDataProviderValid()) { if (EditorUtility.DisplayDialog(dialogTitle, dialogContent, SpriteEditorWindowStyles.applyButtonLabel.text, SpriteEditorWindowStyles.revertButtonLabel.text)) @@ -364,10 +364,15 @@ void HandleApplyRevertDialog(string dialogTitle, string dialogContent) } } + bool IsSpriteDataProviderValid() + { + return m_SpriteDataProvider != null && !m_SpriteDataProvider.Equals(null); + } + void RefreshRects() { m_RectsCache = null; - if (m_SpriteDataProvider != null) + if (IsSpriteDataProviderValid()) { m_RectsCache = m_SpriteDataProvider.GetSpriteRects().ToList(); } @@ -379,7 +384,7 @@ private void Update() { if (m_ResetOnNextRepaint || selectedProviderChanged) { - if (selectedProviderChanged || m_SpriteDataProvider == null) + if (selectedProviderChanged || !IsSpriteDataProviderValid()) m_SelectedAssetPath = GetSelectionAssetPath(); RebuildCache(); } @@ -393,7 +398,6 @@ private void RebuildCache() RefreshPropertiesCache(); RefreshRects(); UpdateAvailableModules(); - SetupModule(m_CurrentModuleIndex); } private void DoTextureAndModulesGUI() @@ -735,7 +739,7 @@ void UpdateAvailableModules() } m_RegisteredModuleNames = new GUIContent[m_RegisteredModules.Count]; - int lastUsedModuleIndex = -1; + int lastUsedModuleIndex = 0; for (int i = 0; i < m_RegisteredModules.Count; i++) { m_RegisteredModuleNames[i] = new GUIContent(m_RegisteredModules[i].moduleName); @@ -745,10 +749,7 @@ void UpdateAvailableModules() } } - if (lastUsedModuleIndex >= 0) - SetupModule(lastUsedModuleIndex); - else - SetupModule(0); + SetupModule(lastUsedModuleIndex); } void InitModules() diff --git a/Editor/Mono/TooltipView/TooltipView.cs b/Editor/Mono/TooltipView/TooltipView.cs index 4a9b6242c8..bc3f53b210 100644 --- a/Editor/Mono/TooltipView/TooltipView.cs +++ b/Editor/Mono/TooltipView/TooltipView.cs @@ -66,7 +66,7 @@ void Setup(string tooltip, Rect rect) position = new Rect(0, 0, m_optimalSize.x, m_optimalSize.y); - window.ShowPopup(); + window.ShowTooltip(); window.SetAlpha(1.0f); s_guiView.mouseRayInvisible = true; diff --git a/Editor/Mono/UIElements/Controls/CurveField.cs b/Editor/Mono/UIElements/Controls/CurveField.cs index a6c6f02794..9bfd43d676 100644 --- a/Editor/Mono/UIElements/Controls/CurveField.cs +++ b/Editor/Mono/UIElements/Controls/CurveField.cs @@ -149,6 +149,11 @@ public override void SetValueWithoutNotify(AnimationCurve newValue) m_Value.postWrapMode = WrapMode.Once; } m_TextureDirty = true; + if (CurveEditorWindow.visible && Object.ReferenceEquals(CurveEditorWindow.curve, m_Value)) + { + CurveEditorWindow.curve = m_Value; + CurveEditorWindow.instance.Repaint(); + } IncrementVersion(VersionChangeType.Repaint); @@ -177,10 +182,10 @@ void ShowCurveEditor() CurveEditorSettings settings = new CurveEditorSettings(); if (m_Value == null) m_Value = new AnimationCurve(); + CurveEditorWindow.instance.Show(OnCurveChanged, settings); CurveEditorWindow.curve = m_Value; CurveEditorWindow.color = curveColor; - CurveEditorWindow.instance.Show(OnCurveChanged, settings); } protected internal override void ExecuteDefaultAction(EventBase evt) @@ -237,8 +242,8 @@ void FillCurveData() normals = new Vector3[k_HorizontalCurveResolution * 2]; } - float startTime = 0; - float endTime = curve.keys.Length > 0 ? curve.keys[curve.keys.Length - 1].time : 1.0f; + float startTime = curve.keys[0].time; + float endTime = curve.keys[curve.keys.Length - 1].time; float duration = endTime - startTime; float minValue = Mathf.Infinity; diff --git a/Editor/Mono/UIElements/UXMLEditorFactories.cs b/Editor/Mono/UIElements/UXMLEditorFactories.cs index e08c7e366c..db9c6cd070 100644 --- a/Editor/Mono/UIElements/UXMLEditorFactories.cs +++ b/Editor/Mono/UIElements/UXMLEditorFactories.cs @@ -6,11 +6,12 @@ namespace UnityEditor.Experimental.UIElements { + [InitializeOnLoad] internal class UXMLEditorFactories { private static bool s_Registered; - internal static void RegisterAll() + static UXMLEditorFactories() { if (s_Registered) return; diff --git a/Editor/Mono/UIElements/VisualTreeAssetEditor.cs b/Editor/Mono/UIElements/VisualTreeAssetEditor.cs index 230c2b81b2..dd9f1f6885 100644 --- a/Editor/Mono/UIElements/VisualTreeAssetEditor.cs +++ b/Editor/Mono/UIElements/VisualTreeAssetEditor.cs @@ -59,7 +59,6 @@ public void Render(VisualTreeAsset vta, Rect r, GUIStyle background) if (m_Panel == null) { - UXMLEditorFactories.RegisterAll(); m_Panel = UIElementsUtility.FindOrCreatePanel(m_LastTree, ContextType.Editor, new DataWatchService()); if (m_Panel.visualTree.styleSheets == null) { diff --git a/Editor/Mono/Undo/Undo.bindings.cs b/Editor/Mono/Undo/Undo.bindings.cs index 094c9980e3..c9db0a16eb 100644 --- a/Editor/Mono/Undo/Undo.bindings.cs +++ b/Editor/Mono/Undo/Undo.bindings.cs @@ -78,9 +78,11 @@ public static void DestroyObjectImmediate(Object objectToUndo) DestroyObjectUndoable(objectToUndo, "Destroy Object"); } - [FreeFunction] + [NativeThrows] + [StaticAccessor("UndoBindings", StaticAccessorType.DoubleColon)] internal static extern void DestroyObjectUndoable([NotNull] Object objectToUndo, string name); + [NativeThrows] [StaticAccessor("UndoBindings", StaticAccessorType.DoubleColon)] public static extern Component AddComponent([NotNull] GameObject gameObject, Type type); diff --git a/Editor/Mono/Utils/DirectoryExtensions.cs b/Editor/Mono/Utils/DirectoryExtensions.cs index 7079909df6..609bbae427 100644 --- a/Editor/Mono/Utils/DirectoryExtensions.cs +++ b/Editor/Mono/Utils/DirectoryExtensions.cs @@ -6,7 +6,7 @@ namespace UnityEditor.Utils { - internal static class DirectoryExtensions + static class DirectoryExtensions { public static void DeleteRecursive(this string directoryPath) { diff --git a/Editor/Mono/Utils/StateCache.cs b/Editor/Mono/Utils/StateCache.cs index f509b1a376..e24a23ac21 100644 --- a/Editor/Mono/Utils/StateCache.cs +++ b/Editor/Mono/Utils/StateCache.cs @@ -2,74 +2,126 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System; using System.Collections.Generic; using System.Text; +using UnityEditor.Utils; using UnityEngine; namespace UnityEditor { - class StateCache + class StateCache { - string m_CacheFolder = "Library/StateCache/"; - Dictionary m_Cache = new Dictionary(); + string m_CacheFolder; + Dictionary m_Cache = new Dictionary(); - public StateCache() + public StateCache(string cacheFolder) { - } + if (string.IsNullOrEmpty(cacheFolder)) + throw new ArgumentException("cacheFolder cannot be null or empty string", cacheFolder); - public StateCache(string cacheFolderPath) - { - if (ValidateCacheFolder(cacheFolderPath)) - m_CacheFolder = cacheFolderPath; + if (cacheFolder.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0) + { + throw new ArgumentException("Cache folder path has invalid path characters: '" + cacheFolder + "'"); + } + + cacheFolder = cacheFolder.ConvertSeparatorsToUnity(); + if (!cacheFolder.EndsWith("/")) + { + Debug.LogError("The cache folder path should end with a forward slash: '/'. Path: " + cacheFolder + ". Fixed up."); + cacheFolder += "/"; + } + if (cacheFolder.StartsWith("/")) + { + Debug.LogError("The cache folder path should not start with a forward slash: '/'. Path: " + cacheFolder + ". Fixed up."); // since on OSX a leading '/' means the root directory + cacheFolder = cacheFolder.TrimStart(new[] { '/' }); + } + + m_CacheFolder = cacheFolder; } - public void SetState(string key, T obj) + public string cacheFolderPath { get { return m_CacheFolder; } } + + public void SetState(string key, T obj) { + if (string.IsNullOrEmpty(key)) + throw new ArgumentException("key cannot be null or empty string", key); + + if (obj == null) + throw new ArgumentNullException("obj"); + string json = JsonUtility.ToJson(obj); - m_Cache[key] = json; - var filePath = GetFilePath(key); - string directory = System.IO.Path.GetDirectoryName(filePath); - System.IO.Directory.CreateDirectory(directory); - System.IO.File.WriteAllText(filePath, json, Encoding.UTF8); // Persist state + var filePath = GetFilePathForKey(key); + try + { + string directory = System.IO.Path.GetDirectoryName(filePath); + System.IO.Directory.CreateDirectory(directory); + System.IO.File.WriteAllText(filePath, json, Encoding.UTF8); // Persist state + } + catch (Exception e) + { + Debug.LogError(string.Format("Error saving file {0}. Error: {1}", filePath, e.ToString())); + } + + m_Cache[key] = obj; } - public T GetState(string key) + public T GetState(string key) { - string jsonString; - if (!m_Cache.TryGetValue(key, out jsonString)) + if (string.IsNullOrEmpty(key)) + throw new ArgumentException("key cannot be null or empty string", key); + + T obj; + if (m_Cache.TryGetValue(key, out obj)) + return obj; + + string filePath = GetFilePathForKey(key); + if (System.IO.File.Exists(filePath)) { - if (System.IO.File.Exists(GetFilePath(key))) - jsonString = System.IO.File.ReadAllText(GetFilePath(key)); // Fetch persisted state + string jsonString = null; + try + { + jsonString = System.IO.File.ReadAllText(filePath, Encoding.UTF8); + } + catch (Exception e) + { + Debug.LogError(string.Format("Error loading file {0}. Error: {1}", filePath, e.ToString())); + return default(T); + } + + try + { + obj = JsonUtility.FromJson(jsonString); + } + catch (ArgumentException exception) + { + Debug.LogError(string.Format("Invalid file content for {0}. Removing file. Error: {1}", filePath, exception.ToString())); + RemoveState(key); + return default(T); + } + + m_Cache[key] = obj; + return obj; } - return JsonUtility.FromJson(jsonString); + + return default(T); } public void RemoveState(string key) { + if (string.IsNullOrEmpty(key)) + throw new ArgumentException("key cannot be null or empty string", key); + m_Cache.Remove(key); - System.IO.File.Delete(GetFilePath(key)); - } - string GetFilePath(string fileName) - { - return m_CacheFolder + fileName + ".json"; + string filePath = GetFilePathForKey(key); + if (System.IO.File.Exists(filePath)) + System.IO.File.Delete(filePath); } - bool ValidateCacheFolder(string cacheFolder) + public string GetFilePathForKey(string key) { - if (string.IsNullOrEmpty(cacheFolder)) - { - Debug.LogError("Invalid cache folder: Is null or empty"); - return false; - } - - if (!m_CacheFolder.EndsWith("/")) - { - Debug.LogError("The cache folder path provided should end with a forward slash: '/'. Path provided: " + m_CacheFolder); - return false; - } - - return true; + return m_CacheFolder + key + ".json"; } } } diff --git a/Editor/Mono/VisualStudioIntegration/SolutionSynchronizer.cs b/Editor/Mono/VisualStudioIntegration/SolutionSynchronizer.cs index 5392398c9f..998704dfca 100644 --- a/Editor/Mono/VisualStudioIntegration/SolutionSynchronizer.cs +++ b/Editor/Mono/VisualStudioIntegration/SolutionSynchronizer.cs @@ -18,8 +18,8 @@ using UnityEditorInternal; using UnityEditor.Scripting.Compilers; -using Mono.Cecil; using UnityEditor.Compilation; +using UnityEditor.Modules; namespace UnityEditor.VisualStudioIntegration { @@ -205,6 +205,12 @@ private bool ShouldSyncOnReimportedAsset(string asset) public void Sync() { + // Do not sync solution until all Unity extensions are registered and initialized. + // Otherwise Unity might emit errors when VSTU tries to generate the solution and + // get all managed extensions, which not yet initialized. + if (!InternalEditorUtility.IsUnityExtensionsInitialized()) + return; + SetupProjectSupportedExtensions(); bool externalCodeAlreadyGeneratedProjects = AssetPostprocessingInternal.OnPreGeneratingCSProjectFiles(); @@ -227,25 +233,44 @@ internal void GenerateAndWriteSolutionAndProjects(ScriptEditorUtility.ScriptEdit var allAssetProjectParts = GenerateAllAssetProjectParts(); - var responseFilePath = Path.Combine("Assets", MonoCSharpCompiler.ReponseFilename); + var responseFilePath = Path.Combine("Assets", MonoCSharpCompiler.ResponseFilename); - var responseFileData = ScriptCompilerBase.ParseResponseFileFromFile(Path.Combine(_projectDirectory, responseFilePath)); + var monoIslands = islands.ToList(); - if (responseFileData.Errors.Length > 0) - { - foreach (var error in responseFileData.Errors) - UnityEngine.Debug.LogErrorFormat("{0} Parse Error : {1}", responseFilePath, error); - } - SyncSolution(islands); - var allProjectIslands = RelevantIslandsForMode(islands, ModeForCurrentExternalEditor()).ToList(); + SyncSolution(monoIslands); + var allProjectIslands = RelevantIslandsForMode(monoIslands, ModeForCurrentExternalEditor()).ToList(); foreach (MonoIsland island in allProjectIslands) + { + var responseFileData = parseResponseFileData(island, responseFilePath); SyncProject(island, allAssetProjectParts, responseFileData, allProjectIslands); + } if (scriptEditor == ScriptEditorUtility.ScriptEditor.VisualStudioCode) WriteVSCodeSettingsFiles(); } + ScriptCompilerBase.ResponseFileData parseResponseFileData(MonoIsland island, string responseFilePath) + { + var systemReferenceDirectories = CSharpLanguage.GetCSharpCompiler(island._target, true, "Assembly-CSharp") == CSharpCompiler.Microsoft + && PlayerSettings.GetScriptingBackend(BuildPipeline.GetBuildTargetGroup(island._target)) == ScriptingImplementation.WinRTDotNET + ? MicrosoftCSharpCompiler.GetClassLibraries(island._target) + : MonoLibraryHelpers.GetSystemReferenceDirectories(island._api_compatibility_level); + + ScriptCompilerBase.ResponseFileData responseFileData = ScriptCompilerBase.ParseResponseFileFromFile( + Path.Combine(_projectDirectory, responseFilePath), + _projectDirectory, + systemReferenceDirectories); + + if (responseFileData.Errors.Length > 0) + { + foreach (var error in responseFileData.Errors) + UnityEngine.Debug.LogErrorFormat("{0} Parse Error : {1}", responseFilePath, error); + } + + return responseFileData; + } + Dictionary GenerateAllAssetProjectParts() { Dictionary stringBuilders = new Dictionary(); @@ -395,8 +420,9 @@ string ProjectText(MonoIsland island, projectBuilder.Append(additionalAssetsForProject); var allAdditionalReferenceFilenames = new List(); + var islandRefs = references.Union(island._references); - foreach (string reference in references.Union(island._references).Union(responseFileData.References.Select(r => r.Assembly))) + foreach (string reference in islandRefs) { if (reference.EndsWith("/UnityEditor.dll") || reference.EndsWith("/UnityEngine.dll") || reference.EndsWith("\\UnityEditor.dll") || reference.EndsWith("\\UnityEngine.dll")) continue; @@ -433,13 +459,13 @@ string ProjectText(MonoIsland island, allAdditionalReferenceFilenames.Add(referenceName); } - //replace \ with / and \\ with / - var escapedFullPath = SecurityElement.Escape(fullReference); - escapedFullPath = escapedFullPath.Replace("\\", "/"); - escapedFullPath = escapedFullPath.Replace("\\\\", "/"); - projectBuilder.AppendFormat(" {1}", Path.GetFileNameWithoutExtension(escapedFullPath), WindowsNewline); - projectBuilder.AppendFormat(" {0}{1}", escapedFullPath, WindowsNewline); - projectBuilder.AppendFormat(" {0}", WindowsNewline); + AppendReference(fullReference, projectBuilder); + } + + var responseRefs = responseFileData.FullPathReferences.Select(r => r.Assembly); + foreach (var reference in responseRefs) + { + AppendReference(reference, projectBuilder); } if (0 < projectReferences.Count) @@ -466,6 +492,17 @@ string ProjectText(MonoIsland island, return projectBuilder.ToString(); } + static void AppendReference(string fullReference, StringBuilder projectBuilder) + { + //replace \ with / and \\ with / + var escapedFullPath = SecurityElement.Escape(fullReference); + escapedFullPath = escapedFullPath.Replace("\\", "/"); + escapedFullPath = escapedFullPath.Replace("\\\\", "/"); + projectBuilder.AppendFormat(" {1}", Path.GetFileNameWithoutExtension(escapedFullPath), WindowsNewline); + projectBuilder.AppendFormat(" {0}{1}", escapedFullPath, WindowsNewline); + projectBuilder.AppendFormat(" {0}", WindowsNewline); + } + public string ProjectFile(MonoIsland island) { ScriptingLanguage language = ScriptingLanguageFor(island); @@ -498,7 +535,7 @@ private string ProjectHeader(MonoIsland island, { targetframeworkversion = "v4.7.1"; } - targetLanguageVersion = "7.2"; + targetLanguageVersion = "latest"; } else if (_settings.VisualStudioVersion == 9) { diff --git a/Editor/Src/VR/Mono/GoogleVR/VRCustomOptionsGoogleVR.cs b/Editor/Src/VR/Mono/GoogleVR/VRCustomOptionsGoogleVR.cs index 9f52de4a7e..3eb316accf 100644 --- a/Editor/Src/VR/Mono/GoogleVR/VRCustomOptionsGoogleVR.cs +++ b/Editor/Src/VR/Mono/GoogleVR/VRCustomOptionsGoogleVR.cs @@ -47,7 +47,7 @@ public override Rect Draw(BuildTargetGroup target, Rect rect) return rect; } - public override float GetHeight() + public override float GetHeight(BuildTargetGroup target) { return EditorGUIUtility.singleLineHeight + (EditorGUIUtility.standardVerticalSpacing * 2.0f); } @@ -87,9 +87,9 @@ public override Rect Draw(BuildTargetGroup target, Rect rect) return rect; } - public override float GetHeight() + public override float GetHeight(BuildTargetGroup target) { - return base.GetHeight() + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + return base.GetHeight(target) + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; } } @@ -266,7 +266,7 @@ public override Rect Draw(BuildTargetGroup target, Rect rect) return rect; } - public override float GetHeight() + public override float GetHeight(BuildTargetGroup target) { float singleLineCount = 5.0f; float thumbnailCount = 2.0f; @@ -278,7 +278,7 @@ public override float GetHeight() verticalSpacingCount += 1.0f; } - return base.GetHeight() + (EditorGUIUtility.singleLineHeight * singleLineCount) + + return base.GetHeight(target) + (EditorGUIUtility.singleLineHeight * singleLineCount) + (EditorGUI.kObjectFieldThumbnailHeight * thumbnailCount) + (EditorGUIUtility.standardVerticalSpacing * verticalSpacingCount); } diff --git a/Editor/Src/VR/Mono/Oculus/VRCustomOptionsOculus.cs b/Editor/Src/VR/Mono/Oculus/VRCustomOptionsOculus.cs index 162bf65800..a76b51cb62 100644 --- a/Editor/Src/VR/Mono/Oculus/VRCustomOptionsOculus.cs +++ b/Editor/Src/VR/Mono/Oculus/VRCustomOptionsOculus.cs @@ -13,6 +13,7 @@ internal class VRCustomOptionsOculus : VRCustomOptions { static GUIContent s_SharedDepthBufferLabel = EditorGUIUtility.TextContent("Shared Depth Buffer|Enable depth buffer submission to allow for overlay depth occlusion, etc."); static GUIContent s_DashSupportLabel = EditorGUIUtility.TextContent("Dash Support|If enabled, pressing the home button brings up Dash, otherwise it brings up the older universal menu."); + static GUIContent s_SharedDepthAndroidInfo = EditorGUIUtility.TrTextContent("Shared Depth Buffer and Dash Support aren't available when targeting mobile."); SerializedProperty m_SharedDepthBuffer; SerializedProperty m_DashSupport; @@ -33,39 +34,51 @@ public override Rect Draw(BuildTargetGroup target, Rect rect) { rect.y += EditorGUIUtility.standardVerticalSpacing; - EditorGUI.BeginDisabled(target == BuildTargetGroup.Android); - - rect.height = EditorGUIUtility.singleLineHeight; - GUIContent label = EditorGUI.BeginProperty(rect, s_SharedDepthBufferLabel, m_SharedDepthBuffer); - EditorGUI.BeginChangeCheck(); - bool boolValue = EditorGUI.Toggle(rect, label, m_SharedDepthBuffer.boolValue); - if (EditorGUI.EndChangeCheck()) + if (target != BuildTargetGroup.Android) { - m_SharedDepthBuffer.boolValue = boolValue; - } - EditorGUI.EndProperty(); - rect.y += rect.height + EditorGUIUtility.standardVerticalSpacing; + rect.height = EditorGUIUtility.singleLineHeight; + GUIContent label = EditorGUI.BeginProperty(rect, s_SharedDepthBufferLabel, m_SharedDepthBuffer); + EditorGUI.BeginChangeCheck(); + bool boolValue = EditorGUI.Toggle(rect, label, m_SharedDepthBuffer.boolValue); + if (EditorGUI.EndChangeCheck()) + { + m_SharedDepthBuffer.boolValue = boolValue; + } + EditorGUI.EndProperty(); + rect.y += rect.height + EditorGUIUtility.standardVerticalSpacing; - rect.height = EditorGUIUtility.singleLineHeight; - label = EditorGUI.BeginProperty(rect, s_DashSupportLabel, m_DashSupport); - EditorGUI.BeginChangeCheck(); - boolValue = EditorGUI.Toggle(rect, label, m_DashSupport.boolValue); - if (EditorGUI.EndChangeCheck()) + rect.height = EditorGUIUtility.singleLineHeight; + label = EditorGUI.BeginProperty(rect, s_DashSupportLabel, m_DashSupport); + EditorGUI.BeginChangeCheck(); + boolValue = EditorGUI.Toggle(rect, label, m_DashSupport.boolValue); + if (EditorGUI.EndChangeCheck()) + { + m_DashSupport.boolValue = boolValue; + } + EditorGUI.EndProperty(); + } + else { - m_DashSupport.boolValue = boolValue; + EditorGUI.BeginDisabled(true); + EditorGUI.LabelField(rect, s_SharedDepthAndroidInfo.text, EditorStyles.wordWrappedMiniLabel); + EditorGUI.EndDisabled(); } - EditorGUI.EndProperty(); - - EditorGUI.EndDisabled(); rect.y += rect.height + EditorGUIUtility.standardVerticalSpacing; return rect; } - public override float GetHeight() + public override float GetHeight(BuildTargetGroup target) { - return (EditorGUIUtility.singleLineHeight * 2.0f) + (EditorGUIUtility.standardVerticalSpacing * 3.0f); + if (target != BuildTargetGroup.Android) + { + return (EditorGUIUtility.singleLineHeight * 2.0f) + (EditorGUIUtility.standardVerticalSpacing * 3.0f); + } + else + { + return (EditorGUIUtility.singleLineHeight * 1.0f) + (EditorGUIUtility.standardVerticalSpacing * 2.0f); + } } } } diff --git a/Editor/Src/VR/Mono/PlayerSettingsEditorVR.cs b/Editor/Src/VR/Mono/PlayerSettingsEditorVR.cs index 69162be0eb..82b85d1157 100644 --- a/Editor/Src/VR/Mono/PlayerSettingsEditorVR.cs +++ b/Editor/Src/VR/Mono/PlayerSettingsEditorVR.cs @@ -22,19 +22,21 @@ static class Styles { public static readonly GUIContent singlepassAndroidWarning = EditorGUIUtility.TrTextContent("Single Pass stereo rendering requires OpenGL ES 3. Please make sure that it's the first one listed under Graphics APIs."); public static readonly GUIContent singlepassAndroidWarning2 = EditorGUIUtility.TrTextContent("Multi Pass will be used on Android devices that don't support Single Pass."); + public static readonly GUIContent singlepassAndroidWarning3 = EditorGUIUtility.TrTextContent("When using a Scriptable Render Pipeline, Single Pass Double Wide will be used on Android devices that don't support Single Pass Instancing or Multi-view."); public static readonly GUIContent singlePassInstancedWarning = EditorGUIUtility.TrTextContent("Single Pass Instanced is only supported on Windows. Multi Pass will be used on other platforms."); + public static readonly GUIContent multiPassNotSupportedWithSRP = EditorGUIUtility.TrTextContent("Multi Pass is only supported using the legacy render pipelies. Stereo Rendering Mode is set to the fallback mode of Single Pass."); public static readonly GUIContent[] kDefaultStereoRenderingPaths = { EditorGUIUtility.TrTextContent("Multi Pass"), EditorGUIUtility.TrTextContent("Single Pass"), - EditorGUIUtility.TrTextContent("Single Pass Instanced (Preview)") + EditorGUIUtility.TrTextContent("Single Pass Instanced") }; public static readonly GUIContent[] kMultiviewStereoRenderingPaths = { EditorGUIUtility.TrTextContent("Multi Pass"), - EditorGUIUtility.TrTextContent("Single Pass Multiview or Instanced (Preview)") + EditorGUIUtility.TrTextContent("Single Pass") }; public static readonly GUIContent xrSettingsTitle = EditorGUIUtility.TrTextContent("XR Settings"); @@ -60,6 +62,7 @@ static class Styles private bool m_InstallsRequired = false; private bool m_VuforiaInstalled = false; + private bool m_ShowMultiPassSRPInfoBox = false; internal int GUISectionIndex { get; set; } @@ -101,6 +104,7 @@ private void RefreshVRDeviceList(BuildTargetGroup targetGroup) customOptions = new VRCustomOptionsNone(); } customOptions.Initialize(m_Settings.serializedObject); + customOptions.IsExpanded = true; m_CustomOptions.Add(deviceInfo.deviceNameKey, customOptions); } } @@ -159,6 +163,8 @@ internal void XRSectionGUI(BuildTargetGroup targetGroup, int sectionIndex) VuforiaGUI(targetGroup); + RemotingWSAHolographicGUI(targetGroup); + Stereo360CaptureGUI(targetGroup); ErrorOnARDeviceIncompatibility(targetGroup); @@ -169,6 +175,11 @@ internal void XRSectionGUI(BuildTargetGroup targetGroup, int sectionIndex) m_Settings.EndSettingsBox(); } + internal bool TargetGroupSupportsWSAHolographicRemoting(BuildTargetGroup targetGroup) + { + return targetGroup == BuildTargetGroup.WSA; + } + private void DevicesGUI(BuildTargetGroup targetGroup) { if (!TargetGroupSupportsVirtualReality(targetGroup)) @@ -297,16 +308,44 @@ private static GUIContent[] GetStereoRenderingPaths(BuildTargetGroup targetGroup return DoesBuildTargetSupportStereoMultiviewRendering(targetGroup) ? Styles.kMultiviewStereoRenderingPaths : Styles.kDefaultStereoRenderingPaths; } + private bool IsStereoRenderingModeSupported(BuildTargetGroup targetGroup, StereoRenderingPath stereoRenderingPath) + { + switch (stereoRenderingPath) + { + case StereoRenderingPath.MultiPass: + return (UnityEngine.Rendering.GraphicsSettings.renderPipelineAsset == null); + + case StereoRenderingPath.SinglePass: + return DoesBuildTargetSupportSinglePassStereoRendering(targetGroup); + + case StereoRenderingPath.Instancing: + return DoesBuildTargetSupportStereoInstancingRendering(targetGroup); + } + ; + + return false; + } + + void OnStereoModeSelected(SerializedProperty stereoRenderingPath, object userData) + { + stereoRenderingPath.intValue = (int)userData; + m_ShowMultiPassSRPInfoBox = false; + + m_Settings.serializedObject.ApplyModifiedProperties(); + } + private void SinglePassStereoGUI(BuildTargetGroup targetGroup, SerializedProperty stereoRenderingPath) { if (!PlayerSettings.virtualRealitySupported) return; - bool supportsSinglePass = DoesBuildTargetSupportSinglePassStereoRendering(targetGroup); - bool supportsSinglePassInstanced = DoesBuildTargetSupportStereoInstancingRendering(targetGroup); + bool supportsMultiPass = IsStereoRenderingModeSupported(targetGroup, StereoRenderingPath.MultiPass); + bool supportsSinglePass = IsStereoRenderingModeSupported(targetGroup, StereoRenderingPath.SinglePass); + bool supportsSinglePassInstanced = IsStereoRenderingModeSupported(targetGroup, StereoRenderingPath.Instancing); // populate the dropdown with the valid options based on target platform. - int validStereoRenderingOptionsCount = 1 + (supportsSinglePass ? 1 : 0) + (supportsSinglePassInstanced ? 1 : 0); + int multiPassAndSinglePass = 2; + int validStereoRenderingOptionsCount = multiPassAndSinglePass + (supportsSinglePassInstanced ? 1 : 0); var validStereoRenderingPaths = new GUIContent[validStereoRenderingOptionsCount]; var validStereoRenderingValues = new int[validStereoRenderingOptionsCount]; @@ -316,11 +355,8 @@ private void SinglePassStereoGUI(BuildTargetGroup targetGroup, SerializedPropert validStereoRenderingPaths[addedStereoRenderingOptionsCount] = stereoRenderingPaths[(int)StereoRenderingPath.MultiPass]; validStereoRenderingValues[addedStereoRenderingOptionsCount++] = (int)StereoRenderingPath.MultiPass; - if (supportsSinglePass) - { - validStereoRenderingPaths[addedStereoRenderingOptionsCount] = stereoRenderingPaths[(int)StereoRenderingPath.SinglePass]; - validStereoRenderingValues[addedStereoRenderingOptionsCount++] = (int)StereoRenderingPath.SinglePass; - } + validStereoRenderingPaths[addedStereoRenderingOptionsCount] = stereoRenderingPaths[(int)StereoRenderingPath.SinglePass]; + validStereoRenderingValues[addedStereoRenderingOptionsCount++] = (int)StereoRenderingPath.SinglePass; if (supportsSinglePassInstanced) { @@ -329,20 +365,56 @@ private void SinglePassStereoGUI(BuildTargetGroup targetGroup, SerializedPropert } // setup fallbacks + if (!supportsMultiPass && (stereoRenderingPath.intValue == (int)StereoRenderingPath.MultiPass)) + { + stereoRenderingPath.intValue = (int)StereoRenderingPath.SinglePass; + m_ShowMultiPassSRPInfoBox = true; + } + if (!supportsSinglePassInstanced && (stereoRenderingPath.intValue == (int)StereoRenderingPath.Instancing)) stereoRenderingPath.intValue = (int)StereoRenderingPath.SinglePass; if (!supportsSinglePass && (stereoRenderingPath.intValue == (int)StereoRenderingPath.SinglePass)) stereoRenderingPath.intValue = (int)StereoRenderingPath.MultiPass; - EditorGUILayout.IntPopup(stereoRenderingPath, validStereoRenderingPaths, validStereoRenderingValues, EditorGUIUtility.TrTextContent("Stereo Rendering Mode*")); + if (m_ShowMultiPassSRPInfoBox) + EditorGUILayout.HelpBox(Styles.multiPassNotSupportedWithSRP.text, MessageType.Info); + + var rect = EditorGUILayout.GetControlRect(); + EditorGUI.BeginProperty(rect, EditorGUIUtility.TrTextContent("Stereo Rendering Mode*"), stereoRenderingPath); + rect = EditorGUI.PrefixLabel(rect, EditorGUIUtility.TrTextContent("Stereo Rendering Mode*")); + + int index = Math.Max(0, Array.IndexOf(validStereoRenderingValues, stereoRenderingPath.intValue)); + if (EditorGUI.DropdownButton(rect, validStereoRenderingPaths[index], FocusType.Passive)) + { + var menu = new GenericMenu(); + for (int i = 0; i < validStereoRenderingValues.Length; i++) + { + int value = validStereoRenderingValues[i]; + bool selected = (value == stereoRenderingPath.intValue); + + if (!IsStereoRenderingModeSupported(targetGroup, (StereoRenderingPath)value)) + menu.AddDisabledItem(validStereoRenderingPaths[i], selected); + else + menu.AddItem(validStereoRenderingPaths[i], selected, (object userData) => { OnStereoModeSelected(stereoRenderingPath, userData); }, value); + } + menu.DropDown(rect); + } + EditorGUI.EndProperty(); if ((stereoRenderingPath.intValue == (int)StereoRenderingPath.SinglePass) && (targetGroup == BuildTargetGroup.Android)) { var apisAndroid = PlayerSettings.GetGraphicsAPIs(BuildTarget.Android); if ((apisAndroid.Length > 0) && (apisAndroid[0] == GraphicsDeviceType.OpenGLES3)) { - EditorGUILayout.HelpBox(Styles.singlepassAndroidWarning2.text, MessageType.Info); + if (supportsMultiPass) + { + EditorGUILayout.HelpBox(Styles.singlepassAndroidWarning2.text, MessageType.Info); + } + else + { + EditorGUILayout.HelpBox(Styles.singlepassAndroidWarning3.text, MessageType.Info); + } } else { @@ -353,6 +425,8 @@ private void SinglePassStereoGUI(BuildTargetGroup targetGroup, SerializedPropert { EditorGUILayout.HelpBox(Styles.singlePassInstancedWarning.text, MessageType.Warning); } + + m_Settings.serializedObject.ApplyModifiedProperties(); } private void Stereo360CaptureGUI(BuildTargetGroup targetGroup) @@ -390,7 +464,16 @@ private void AddVRDeviceElement(BuildTargetGroup target, Rect rect, ReorderableL private void RemoveVRDeviceElement(BuildTargetGroup target, ReorderableList list) { var devices = VREditor.GetVREnabledDevicesOnTargetGroup(target).ToList(); + var device = devices[list.index]; devices.RemoveAt(list.index); + + VRCustomOptions customOptions; + if (m_CustomOptions.TryGetValue(device, out customOptions)) + { + customOptions.IsExpanded = true; + } + + ApplyChangedVRDeviceList(target, devices.ToArray()); } @@ -464,7 +547,7 @@ private float GetVRDeviceElementHeight(BuildTargetGroup target, int index) VRCustomOptions customOptions; if (m_CustomOptions.TryGetValue(name, out customOptions)) { - customOptionsHeight = customOptions.IsExpanded ? customOptions.GetHeight() + EditorGUI.kControlVerticalSpacing : 0.0f; + customOptionsHeight = customOptions.IsExpanded ? customOptions.GetHeight(target) + EditorGUI.kControlVerticalSpacing : 0.0f; } return list.elementHeight + customOptionsHeight; @@ -560,18 +643,6 @@ internal void TangoGUI(BuildTargetGroup targetGroup) // Google Tango settings EditorGUILayout.PropertyField(m_AndroidEnableTango, EditorGUIUtility.TrTextContent("ARCore Supported")); - - if (PlayerSettings.Android.ARCoreEnabled) - { - EditorGUI.indentLevel++; - - if ((int)PlayerSettings.Android.minSdkVersion < 24) - { - GUIContent tangoAndroidWarning = EditorGUIUtility.TrTextContent("ARCore requires 'Minimum API Level' to be at least Android 7.0"); - EditorGUILayout.HelpBox(tangoAndroidWarning.text, MessageType.Warning); - } - EditorGUI.indentLevel--; - } } internal void VuforiaGUI(BuildTargetGroup targetGroup) @@ -602,5 +673,26 @@ internal void VuforiaGUI(BuildTargetGroup targetGroup) EditorGUILayout.HelpBox("Vuforia Augmented Reality is required when using the Vuforia Virtual Reality SDK.", MessageType.Info); } } + + internal void RemotingWSAHolographicGUI(BuildTargetGroup targetGroup) + { + if (!TargetGroupSupportsWSAHolographicRemoting(targetGroup)) + return; + var shouldEnableScope = VREditor.GetVREnabledOnTargetGroup(targetGroup) && GetVRDeviceElementIsInList(targetGroup, "WindowsMR"); + using (new EditorGUI.DisabledScope(!shouldEnableScope)) + { + var remotingEnabled = PlayerSettings.GetWsaHolographicRemotingEnabled(); + EditorGUI.BeginChangeCheck(); + remotingEnabled = EditorGUILayout.Toggle(EditorGUIUtility.TrTextContent("WSA Holographic Remoting Supported"), remotingEnabled); + if (EditorGUI.EndChangeCheck()) + { + PlayerSettings.SetWsaHolographicRemotingEnabled(remotingEnabled); + } + } + if (shouldEnableScope) + { + EditorGUILayout.HelpBox("WindowsMR is required when using WSA Holographic Remoting.", MessageType.Info); + } + } } } diff --git a/Editor/Src/VR/Mono/VRCustomOptions.cs b/Editor/Src/VR/Mono/VRCustomOptions.cs index 4f2987cfd7..e6b810c3bb 100644 --- a/Editor/Src/VR/Mono/VRCustomOptions.cs +++ b/Editor/Src/VR/Mono/VRCustomOptions.cs @@ -65,12 +65,12 @@ public virtual void Initialize(SerializedObject settings, string propertyName) } abstract public Rect Draw(BuildTargetGroup target, Rect rect); - abstract public float GetHeight(); + abstract public float GetHeight(BuildTargetGroup target); } internal class VRCustomOptionsNone : VRCustomOptions { public override Rect Draw(BuildTargetGroup target, Rect rect) { return rect; } - public override float GetHeight() { return 0.0f; } + public override float GetHeight(BuildTargetGroup target) { return 0.0f; } } } diff --git a/Editor/Src/VR/Mono/WindowsHolographic/VRCustomOptionsWindowsHolographic.cs b/Editor/Src/VR/Mono/WindowsHolographic/VRCustomOptionsWindowsHolographic.cs index 23cacc5175..0dec8b4b95 100644 --- a/Editor/Src/VR/Mono/WindowsHolographic/VRCustomOptionsWindowsHolographic.cs +++ b/Editor/Src/VR/Mono/WindowsHolographic/VRCustomOptionsWindowsHolographic.cs @@ -61,7 +61,7 @@ public override Rect Draw(BuildTargetGroup target, Rect rect) return rect; } - public override float GetHeight() + public override float GetHeight(BuildTargetGroup target) { return EditorGUIUtility.singleLineHeight * 2.0f; } diff --git a/Modules/AI/NavMesh/NavMesh.bindings.cs b/Modules/AI/NavMesh/NavMesh.bindings.cs index 0074c422aa..86023f6ec2 100644 --- a/Modules/AI/NavMesh/NavMesh.bindings.cs +++ b/Modules/AI/NavMesh/NavMesh.bindings.cs @@ -10,7 +10,7 @@ namespace UnityEngine.AI public static partial class NavMesh { [StaticAccessor("GetNavMeshManager()")] - [NativeName("Cleanup")] + [NativeName("CleanupAfterCarving")] public static extern void RemoveAllNavMeshData(); } } diff --git a/Modules/AssetPipelineEditor/ImportSettings/AssetImporterTabbedEditor.cs b/Modules/AssetPipelineEditor/ImportSettings/AssetImporterTabbedEditor.cs index e3ec3ddce3..129af826f3 100644 --- a/Modules/AssetPipelineEditor/ImportSettings/AssetImporterTabbedEditor.cs +++ b/Modules/AssetPipelineEditor/ImportSettings/AssetImporterTabbedEditor.cs @@ -88,6 +88,8 @@ public override void OnInspectorGUI() // the activeTab can get destroyed when opening particular sub-editors (such as the Avatar configuration editor on the Rig tab) if (activeTab != null) { + GUILayout.Space(EditorGUI.kSpacing); + activeTab.OnInspectorGUI(); } diff --git a/Modules/AssetPipelineEditor/ImportSettings/ModelImporterEditor.cs b/Modules/AssetPipelineEditor/ImportSettings/ModelImporterEditor.cs index f11f5c3c8d..af951453d3 100644 --- a/Modules/AssetPipelineEditor/ImportSettings/ModelImporterEditor.cs +++ b/Modules/AssetPipelineEditor/ImportSettings/ModelImporterEditor.cs @@ -11,6 +11,8 @@ namespace UnityEditor [CanEditMultipleObjects] internal class ModelImporterEditor : AssetImporterTabbedEditor { + static string s_LocalizedTitle = L10n.Tr("Model Import Settings"); + public override void OnEnable() { if (tabs == null) @@ -47,5 +49,16 @@ public override GUIContent GetPreviewTitle() // Only show the imported GameObject when the Model tab is active; not when the Animation tab is active public override bool showImportedObject { get { return activeTab is ModelImporterModelEditor; } } + + internal override string targetTitle + { + get + { + if (assetTargets == null || assetTargets.Length == 1 || !m_AllowMultiObjectAccess) + return base.targetTitle; + else + return assetTargets.Length + " " + s_LocalizedTitle; + } + } } } diff --git a/Modules/AssetPipelineEditor/ImportSettings/PluginImporterInspector.cs b/Modules/AssetPipelineEditor/ImportSettings/PluginImporterInspector.cs index 66513fcc75..86d0062c02 100644 --- a/Modules/AssetPipelineEditor/ImportSettings/PluginImporterInspector.cs +++ b/Modules/AssetPipelineEditor/ImportSettings/PluginImporterInspector.cs @@ -307,6 +307,7 @@ protected override void ResetValues() } } + ResetCompatability(ref m_CompatibleWithAnyPlatform, (imp => imp.GetCompatibleWithAnyPlatform())); ResetCompatability(ref m_CompatibleWithEditor, (imp => imp.GetCompatibleWithEditor())); ResetCompatability(ref m_AutoReferenced, (imp => !imp.IsExplicitlyReferenced)); // If Any Platform is selected, initialize m_Compatible* variables using compatability function @@ -425,6 +426,13 @@ protected override void Awake() public override void OnEnable() { + m_DefineConstraints = new ReorderableList(m_DefineConstraintState, typeof(DefineConstraint), false, false, true, true); + m_DefineConstraints.drawElementCallback = DrawDefineConstraintListElement; + m_DefineConstraints.onRemoveCallback = RemoveDefineConstraintListElement; + + m_DefineConstraints.elementHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + m_DefineConstraints.headerHeight = 3; + if (!IsEditingPlatformSettingsSupported()) return; @@ -464,13 +472,6 @@ public override void OnEnable() } m_ReferencesUnityEngineModule = importer.HasDiscouragedReferences(); - - m_DefineConstraints = new ReorderableList(m_DefineConstraintState, typeof(DefineConstraint), false, false, true, true); - m_DefineConstraints.drawElementCallback = DrawDefineConstraintListElement; - m_DefineConstraints.onRemoveCallback = RemoveDefineConstraintListElement; - - m_DefineConstraints.elementHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; - m_DefineConstraints.headerHeight = 3; } private void RemoveDefineConstraintListElement(ReorderableList list) diff --git a/Modules/AssetPipelineEditor/Public/PluginImporter.bindings.cs b/Modules/AssetPipelineEditor/Public/PluginImporter.bindings.cs index 4ec47ccaa8..fd4083cfae 100644 --- a/Modules/AssetPipelineEditor/Public/PluginImporter.bindings.cs +++ b/Modules/AssetPipelineEditor/Public/PluginImporter.bindings.cs @@ -213,6 +213,9 @@ public bool GetExcludeFromAnyPlatform(BuildTarget platform) [NativeMethod("SetCompatibleWithEditor")] extern internal void SetCompatibleWithEditorWithBuildTargetsInternal(BuildTargetGroup buildTargetGroup, BuildTarget buildTarget, bool enable); + [NativeMethod("IsCompatibleWithDefines")] + extern internal bool IsCompatibleWithDefines(string[] defines); + internal void SetCompatibleWithEditor(BuildTargetGroup buildTargetGroup, BuildTarget buildTarget, bool enable) { SetCompatibleWithEditorWithBuildTargetsInternal(buildTargetGroup, buildTarget, enable); diff --git a/Modules/CloudServicesSettingsEditor/Analytics/ScriptBindings/AnalyticsSettings.bindings.cs b/Modules/CloudServicesSettingsEditor/Analytics/ScriptBindings/AnalyticsSettings.bindings.cs index ebd2b86384..a35407687d 100644 --- a/Modules/CloudServicesSettingsEditor/Analytics/ScriptBindings/AnalyticsSettings.bindings.cs +++ b/Modules/CloudServicesSettingsEditor/Analytics/ScriptBindings/AnalyticsSettings.bindings.cs @@ -15,6 +15,8 @@ public static partial class AnalyticsSettings public static extern bool testMode { get; set; } + public static extern bool initializeOnStartup { get; set; } + internal static extern void SetEnabledServiceWindow(bool enabled); internal static extern bool enabledForPlatform { get; } diff --git a/Modules/GraphViewEditor/Manipulators/SelectionDragger.cs b/Modules/GraphViewEditor/Manipulators/SelectionDragger.cs index 790fb52601..56e112b8af 100644 --- a/Modules/GraphViewEditor/Manipulators/SelectionDragger.cs +++ b/Modules/GraphViewEditor/Manipulators/SelectionDragger.cs @@ -453,6 +453,8 @@ void MoveElement(GraphElement element, Rect originalPos) var graphView = target as GraphView; if (graphView != null && graphView.graphViewChanged != null) { + KeyValuePair firstPos = m_OriginalPos.First(); + m_GraphViewChange.moveDelta = firstPos.Key.GetPosition().position - firstPos.Value.pos.position; graphView.graphViewChanged(m_GraphViewChange); } } diff --git a/Modules/GraphViewEditor/Views/GraphView.cs b/Modules/GraphViewEditor/Views/GraphView.cs index e65244ab2c..a22ce0220b 100644 --- a/Modules/GraphViewEditor/Views/GraphView.cs +++ b/Modules/GraphViewEditor/Views/GraphView.cs @@ -44,6 +44,7 @@ public struct GraphViewChange // Operations Completed public List movedElements; + public Vector2 moveDelta; } public struct NodeCreationContext diff --git a/Modules/Grid/ScriptBindings/GridLayout.bindings.cs b/Modules/Grid/ScriptBindings/GridLayout.bindings.cs index 7c6abdfbf8..663ef555de 100644 --- a/Modules/Grid/ScriptBindings/GridLayout.bindings.cs +++ b/Modules/Grid/ScriptBindings/GridLayout.bindings.cs @@ -2,6 +2,8 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using RequiredByNativeCodeAttribute = UnityEngine.Scripting.RequiredByNativeCodeAttribute; + using UnityEngine.Bindings; namespace UnityEngine @@ -81,5 +83,8 @@ public extern CellSwizzle cellSwizzle [FreeFunction("GridLayoutBindings::GetLayoutCellCenter", HasExplicitThis = true)] public extern Vector3 GetLayoutCellCenter(); + + [RequiredByNativeCode] + private void DoNothing() {} } } diff --git a/Modules/GridEditor/Managed/GridEditorUtility.cs b/Modules/GridEditor/Managed/GridEditorUtility.cs index 7516832bc6..59891615e0 100644 --- a/Modules/GridEditor/Managed/GridEditorUtility.cs +++ b/Modules/GridEditor/Managed/GridEditorUtility.cs @@ -216,10 +216,10 @@ public static void DrawGridMarquee(GridLayout gridLayout, BoundsInt area, Color Vector3[] cellLocals = { - gridLayout.CellToLocal(new Vector3Int(area.xMin, area.yMin, 0)), - gridLayout.CellToLocalInterpolated(new Vector3(area.xMax - 1 + cellGap.x, area.yMin, 0)), - gridLayout.CellToLocalInterpolated(new Vector3(area.xMax - 1 + cellGap.x, area.yMax - 1 + cellGap.y, 0)), - gridLayout.CellToLocalInterpolated(new Vector3(area.xMin, area.yMax - 1 + cellGap.y, 0)) + gridLayout.CellToLocal(new Vector3Int(area.xMin, area.yMin, area.zMin)), + gridLayout.CellToLocalInterpolated(new Vector3(area.xMax - 1 + cellGap.x, area.yMin, area.zMin)), + gridLayout.CellToLocalInterpolated(new Vector3(area.xMax - 1 + cellGap.x, area.yMax - 1 + cellGap.y, area.zMin)), + gridLayout.CellToLocalInterpolated(new Vector3(area.xMin, area.yMax - 1 + cellGap.y, area.zMin)) }; HandleUtility.ApplyWireMaterial(); @@ -252,34 +252,34 @@ public static void DrawSelectedHexGridArea(GridLayout gridLayout, BoundsInt area int right = horizontalCount; Vector3[] cellOffset = { - Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(0, gridLayout.cellSize.y / 2, 0)), - Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(gridLayout.cellSize.x / 2, gridLayout.cellSize.y / 4, 0)), - Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(gridLayout.cellSize.x / 2, -gridLayout.cellSize.y / 4, 0)), - Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(0, -gridLayout.cellSize.y / 2, 0)), - Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(-gridLayout.cellSize.x / 2, -gridLayout.cellSize.y / 4, 0)), - Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(-gridLayout.cellSize.x / 2, gridLayout.cellSize.y / 4, 0)) + Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(0, gridLayout.cellSize.y / 2, area.zMin)), + Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(gridLayout.cellSize.x / 2, gridLayout.cellSize.y / 4, area.zMin)), + Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(gridLayout.cellSize.x / 2, -gridLayout.cellSize.y / 4, area.zMin)), + Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(0, -gridLayout.cellSize.y / 2, area.zMin)), + Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(-gridLayout.cellSize.x / 2, -gridLayout.cellSize.y / 4, area.zMin)), + Grid.Swizzle(gridLayout.cellSwizzle, new Vector3(-gridLayout.cellSize.x / 2, gridLayout.cellSize.y / 4, area.zMin)) }; // Fill Top and Bottom Vertices for (int x = area.min.x; x < area.max.x; x++) { - cellLocals[bottom++] = gridLayout.CellToLocal(new Vector3Int(x, area.min.y, 0)) + cellOffset[4]; - cellLocals[bottom++] = gridLayout.CellToLocal(new Vector3Int(x, area.min.y, 0)) + cellOffset[3]; - cellLocals[top--] = gridLayout.CellToLocal(new Vector3Int(x, area.max.y - 1, 0)) + cellOffset[0]; - cellLocals[top--] = gridLayout.CellToLocal(new Vector3Int(x, area.max.y - 1, 0)) + cellOffset[1]; + cellLocals[bottom++] = gridLayout.CellToLocal(new Vector3Int(x, area.min.y, area.zMin)) + cellOffset[4]; + cellLocals[bottom++] = gridLayout.CellToLocal(new Vector3Int(x, area.min.y, area.zMin)) + cellOffset[3]; + cellLocals[top--] = gridLayout.CellToLocal(new Vector3Int(x, area.max.y - 1, area.zMin)) + cellOffset[0]; + cellLocals[top--] = gridLayout.CellToLocal(new Vector3Int(x, area.max.y - 1, area.zMin)) + cellOffset[1]; } // Fill first Left and Right Vertices - cellLocals[left--] = gridLayout.CellToLocal(new Vector3Int(area.min.x, area.min.y, 0)) + cellOffset[5]; - cellLocals[top--] = gridLayout.CellToLocal(new Vector3Int(area.max.x - 1, area.max.y - 1, 0)) + cellOffset[2]; + cellLocals[left--] = gridLayout.CellToLocal(new Vector3Int(area.min.x, area.min.y, area.zMin)) + cellOffset[5]; + cellLocals[top--] = gridLayout.CellToLocal(new Vector3Int(area.max.x - 1, area.max.y - 1, area.zMin)) + cellOffset[2]; // Fill Left and Right Vertices for (int y = area.min.y + 1; y < area.max.y; y++) { - cellLocals[left--] = gridLayout.CellToLocal(new Vector3Int(area.min.x, y, 0)) + cellOffset[4]; - cellLocals[left--] = gridLayout.CellToLocal(new Vector3Int(area.min.x, y, 0)) + cellOffset[5]; + cellLocals[left--] = gridLayout.CellToLocal(new Vector3Int(area.min.x, y, area.zMin)) + cellOffset[4]; + cellLocals[left--] = gridLayout.CellToLocal(new Vector3Int(area.min.x, y, area.zMin)) + cellOffset[5]; } for (int y = area.min.y; y < (area.max.y - 1); y++) { - cellLocals[right++] = gridLayout.CellToLocal(new Vector3Int(area.max.x - 1, y, 0)) + cellOffset[2]; - cellLocals[right++] = gridLayout.CellToLocal(new Vector3Int(area.max.x - 1, y, 0)) + cellOffset[1]; + cellLocals[right++] = gridLayout.CellToLocal(new Vector3Int(area.max.x - 1, y, area.zMin)) + cellOffset[2]; + cellLocals[right++] = gridLayout.CellToLocal(new Vector3Int(area.max.x - 1, y, area.zMin)) + cellOffset[1]; } HandleUtility.ApplyWireMaterial(); GL.PushMatrix(); diff --git a/Modules/IMGUI/GUILayout.cs b/Modules/IMGUI/GUILayout.cs index 193026aef4..f204f4e28a 100644 --- a/Modules/IMGUI/GUILayout.cs +++ b/Modules/IMGUI/GUILayout.cs @@ -242,6 +242,11 @@ static public void FlexibleSpace() op.value = 10000; GUILayoutUtility.GetRect(0, 0, GUILayoutUtility.spaceStyle, op); + + if (Event.current.type == EventType.Layout) + { + GUILayoutUtility.current.topLevel.entries[GUILayoutUtility.current.topLevel.entries.Count - 1].consideredForMargin = false; + } } public static void BeginHorizontal(params GUILayoutOption[] options) { BeginHorizontal(GUIContent.none, GUIStyle.none, options); } diff --git a/Modules/IMGUI/GUIStyle.cs b/Modules/IMGUI/GUIStyle.cs index 074efdb66a..7e90f54d74 100644 --- a/Modules/IMGUI/GUIStyle.cs +++ b/Modules/IMGUI/GUIStyle.cs @@ -80,6 +80,11 @@ public GUIStyle() // Constructs GUIStyle identical to given other GUIStyle. public GUIStyle(GUIStyle other) { + if (other == null) + { + Debug.LogError("Copied style is null. Using StyleNotFound instead."); + other = GUISkin.error; + } m_Ptr = Internal_Copy(this, other); } diff --git a/Modules/ImageConversion/ScriptBindings/ImageConversion.bindings.cs b/Modules/ImageConversion/ScriptBindings/ImageConversion.bindings.cs index 86ce9a075e..d5ea21ea9c 100644 --- a/Modules/ImageConversion/ScriptBindings/ImageConversion.bindings.cs +++ b/Modules/ImageConversion/ScriptBindings/ImageConversion.bindings.cs @@ -32,7 +32,7 @@ public static byte[] EncodeToEXR(this Texture2D tex) } [NativeMethod(Name = "ImageConversionBindings::LoadImage", IsFreeFunction = true)] - extern public static bool LoadImage(this Texture2D tex, byte[] data, bool markNonReadable); + extern public static bool LoadImage([NotNull] this Texture2D tex, byte[] data, bool markNonReadable); public static bool LoadImage(this Texture2D tex, byte[] data) { return LoadImage(tex, data, false); diff --git a/Modules/PackageManager/Editor/Managed/PackageManagerCommands.cs b/Modules/PackageManager/Editor/Managed/PackageManagerCommands.cs index 02809aa621..9eb8bfd098 100644 --- a/Modules/PackageManager/Editor/Managed/PackageManagerCommands.cs +++ b/Modules/PackageManager/Editor/Managed/PackageManagerCommands.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; +using UnityEditor; using UnityEditor.PackageManager.Requests; namespace UnityEditor.PackageManager @@ -12,10 +13,15 @@ static class PackageManagerCommands [MenuItem("Help/Reset Packages to defaults", priority = 1000)] public static void ResetProjectPackagesToEditorDefaults() { + if (EditorUtility.DisplayDialog(L10n.Tr("Unity Package Manager"), + L10n.Tr("Resetting packages to defaults will discard any changes you have made and/or remove packages set by the project template.\nThis action may result in compilation errors or a broken project. Are you sure?"), + L10n.Tr("No"), L10n.Tr("Yes"))) + return; + ResetToEditorDefaultsRequest request = Client.ResetToEditorDefaults(); if (request.Status == StatusCode.Failure) { - Debug.LogError("Resetting your project's packages to the editor's default configuration failed. Reason: " + request.Error.message); + Debug.LogError("Resetting packages to defaults failed. Reason: " + request.Error.message); } } } diff --git a/Modules/PresetsEditor/PresetManagerEditor.cs b/Modules/PresetsEditor/PresetManagerEditor.cs index 7a91f1785f..31d983ae32 100644 --- a/Modules/PresetsEditor/PresetManagerEditor.cs +++ b/Modules/PresetsEditor/PresetManagerEditor.cs @@ -147,6 +147,7 @@ void DrawElementCallback(Rect rect, int index, bool isActive, bool isFocused) case EventType.MouseDown: if (buttonRect.Contains(Event.current.mousePosition)) { + RefreshAddList(); var menu = new GenericMenu(); if (m_DiscoveredPresets.ContainsKey(keyType)) { diff --git a/Modules/ProfilerEditor/MemoryProfiler/MemorySnapshot.cs b/Modules/ProfilerEditor/MemoryProfiler/MemorySnapshot.cs index c61d4a7ef8..0ad5078c77 100644 --- a/Modules/ProfilerEditor/MemoryProfiler/MemorySnapshot.cs +++ b/Modules/ProfilerEditor/MemoryProfiler/MemorySnapshot.cs @@ -10,12 +10,10 @@ namespace UnityEditor.Profiling.Memory.Experimental { - // Note: this snapshot is completely serializable by unity's serializer. // !!!!! NOTE: Keep in sync with Runtime\Profiler\MemorySnapshots.cpp - public class PackedMemorySnapshot : ISerializationCallbackReceiver + public class PackedMemorySnapshot : IDisposable { - private static readonly UInt64 kMinSupportedVersion = 7; - + static readonly UInt64 kMinSupportedVersion = 7; public static PackedMemorySnapshot Load(string path) { MemorySnapshotFileReader reader = new MemorySnapshotFileReader(path); @@ -42,7 +40,6 @@ public static void Save(PackedMemorySnapshot snapshot, string writePath) FileUtil.CopyFileIfExists(path, writePath, true); } - [SerializeField] MemorySnapshotFileReader m_Reader = null; public ConnectionEntries connections { get; internal set; } @@ -84,15 +81,6 @@ internal void BuildEntries() typeDescriptions = new TypeDescriptionEntries(m_Reader); } - public void OnBeforeSerialize() - { - } - - public void OnAfterDeserialize() - { - BuildEntries(); - } - internal MemorySnapshotFileReader GetReader() { return m_Reader; @@ -106,11 +94,11 @@ public UInt32 version } } - public UnityEngine.Profiling.Memory.Experimental.MetaData metadata + public MetaData metadata { get { - byte[] array = m_Reader.GetDataSingle(EntryType.Metadata_UserMetadata, ConversionFunctions.ToByteArray); + byte[] array = m_Reader.GetDataSingle(EntryType.Metadata_UserMetadata, ConversionFunctions.ToByteArray); // decoded as // content_data_length // content_data @@ -121,15 +109,21 @@ public UnityEngine.Profiling.Memory.Experimental.MetaData metadata // [opt: screenshot_width ] // [opt: screenshot_height ] // [opt: screenshot_format ] + var data = new UnityEngine.Profiling.Memory.Experimental.MetaData(); - var metaData = new UnityEngine.Profiling.Memory.Experimental.MetaData(); + if (array.Length == 0) + { + data.content = ""; + data.platform = ""; + return data; + } int offset = 0; int dataLength = 0; offset = ReadIntFromByteArray(array, offset, out dataLength); - offset = ReadStringFromByteArray(array, offset, dataLength, out metaData.content); + offset = ReadStringFromByteArray(array, offset, dataLength, out data.content); offset = ReadIntFromByteArray(array, offset, out dataLength); - offset = ReadStringFromByteArray(array, offset, dataLength, out metaData.platform); + offset = ReadStringFromByteArray(array, offset, dataLength, out data.platform); offset = ReadIntFromByteArray(array, offset, out dataLength); if (dataLength > 0) @@ -146,14 +140,14 @@ public UnityEngine.Profiling.Memory.Experimental.MetaData metadata offset = ReadIntFromByteArray(array, offset, out height); offset = ReadIntFromByteArray(array, offset, out format); - metaData.screenshot = new Texture2D(width, height, (TextureFormat)format, false); - metaData.screenshot.LoadRawTextureData(screenshot); - metaData.screenshot.Apply(); + data.screenshot = new Texture2D(width, height, (TextureFormat)format, false); + data.screenshot.LoadRawTextureData(screenshot); + data.screenshot.Apply(); } UnityEngine.Assertions.Assert.AreEqual(array.Length, offset); - return metaData; + return data; } } @@ -222,7 +216,7 @@ public DateTime recordDate } } - public UnityEngine.Profiling.Memory.Experimental.CaptureFlags captureFlags + public CaptureFlags captureFlags { get { @@ -237,6 +231,31 @@ public VirtualMachineInformation virtualMachineInformation return m_Reader.GetDataSingle(EntryType.Metadata_VirtualMachineInformation, ConversionFunctions.ToVirtualMachineInformation); } } + + ~PackedMemorySnapshot() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (m_Reader == null) + { + return; + } + + m_Reader.Dispose(); + m_Reader = null; + } + } } [Flags] diff --git a/Modules/ProfilerEditor/ProfilerWindow/MemorySnapshot.cs b/Modules/ProfilerEditor/ProfilerWindow/MemorySnapshot.cs index 5ff7053662..5a6c077a9e 100644 --- a/Modules/ProfilerEditor/ProfilerWindow/MemorySnapshot.cs +++ b/Modules/ProfilerEditor/ProfilerWindow/MemorySnapshot.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using System.IO; using UnityEngine; namespace UnityEditor.MemoryProfiler @@ -15,23 +16,33 @@ private static void SnapshotFinished(string path, bool result) { if (result) { - UnityEditor.Profiling.Memory.Experimental.PackedMemorySnapshot snapshot = UnityEditor.Profiling.Memory.Experimental.PackedMemorySnapshot.Load(path); + Profiling.Memory.Experimental.PackedMemorySnapshot snapshot = Profiling.Memory.Experimental.PackedMemorySnapshot.Load(path); - OnSnapshotReceived(new PackedMemorySnapshot(snapshot)); + var oldSnapshot = new PackedMemorySnapshot(snapshot); + snapshot.Dispose(); + File.Delete(path); + + OnSnapshotReceived(oldSnapshot); } else { + if (File.Exists(path)) + File.Delete(path); + OnSnapshotReceived(null); } } - public static void RequestNewSnapshot() + internal static string GetTemporarySnapshotPath() { string[] s = Application.dataPath.Split('/'); string projectName = s[s.Length - 2]; - string path = Application.temporaryCachePath + "/" + projectName + ".snap"; + return Path.Combine(Application.temporaryCachePath, projectName + ".snap"); + } - UnityEngine.Profiling.Memory.Experimental.MemoryProfiler.TakeSnapshot(path, SnapshotFinished, UnityEngine.Profiling.Memory.Experimental.CaptureFlags.NativeObjects | UnityEngine.Profiling.Memory.Experimental.CaptureFlags.ManagedObjects); + public static void RequestNewSnapshot() + { + UnityEngine.Profiling.Memory.Experimental.MemoryProfiler.TakeSnapshot(GetTemporarySnapshotPath(), SnapshotFinished, UnityEngine.Profiling.Memory.Experimental.CaptureFlags.NativeObjects | UnityEngine.Profiling.Memory.Experimental.CaptureFlags.ManagedObjects); } } diff --git a/Modules/ProfilerEditor/ProfilerWindow/NetworkDetailStats.cs b/Modules/ProfilerEditor/ProfilerWindow/NetworkDetailStats.cs index 16ccf7806d..e8ba9d056b 100644 --- a/Modules/ProfilerEditor/ProfilerWindow/NetworkDetailStats.cs +++ b/Modules/ProfilerEditor/ProfilerWindow/NetworkDetailStats.cs @@ -119,8 +119,10 @@ public void NewProfilerTick(int tickId) { entry.NewProfilerTick(tickId); } +#pragma warning disable 618 NetworkTransport.SetPacketStat(0, MsgId, (int)totalIn, 1); NetworkTransport.SetPacketStat(1, MsgId, (int)totalOut, 1); +#pragma warning restore 618 totalIn = 0; totalOut = 0; } @@ -253,8 +255,10 @@ static public void ResetAll() { foreach (var detail in m_NetworkOperations.Values) { +#pragma warning disable 618 NetworkTransport.SetPacketStat(0, detail.MsgId, 0, 1); NetworkTransport.SetPacketStat(1, detail.MsgId, 0, 1); +#pragma warning restore 618 } m_NetworkOperations.Clear(); } diff --git a/Modules/ProfilerEditor/ProfilerWindow/ProfilerFrameHierarchyView.cs b/Modules/ProfilerEditor/ProfilerWindow/ProfilerFrameHierarchyView.cs index 4850a99fb8..f7f1f41a83 100644 --- a/Modules/ProfilerEditor/ProfilerWindow/ProfilerFrameHierarchyView.cs +++ b/Modules/ProfilerEditor/ProfilerWindow/ProfilerFrameHierarchyView.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using UnityEditor; using UnityEditor.IMGUI.Controls; +using Unity.Profiling; namespace UnityEditorInternal.Profiling { @@ -111,6 +112,8 @@ public bool sortedProfilerColumnAscending public delegate void SearchChangedCallback(string newSearch); public event SearchChangedCallback searchChanged; + static readonly ProfilerMarker m_DoGUIMarker = new ProfilerMarker(nameof(ProfilerFrameDataHierarchyView) + ".DoGUI"); + public ProfilerFrameDataHierarchyView() { m_Initialized = false; @@ -275,70 +278,73 @@ static string GetProfilerColumnName(ProfilerColumn column) public void DoGUI(FrameDataView frameDataView) { - InitIfNeeded(); + using (m_DoGUIMarker.Auto()) + { + InitIfNeeded(); - var collectingSamples = ProfilerDriver.enabled && (ProfilerDriver.profileEditor || EditorApplication.isPlaying); - var isSearchAllowed = string.IsNullOrEmpty(treeView.searchString) || !(collectingSamples && ProfilerDriver.deepProfiling); + var collectingSamples = ProfilerDriver.enabled && (ProfilerDriver.profileEditor || EditorApplication.isPlaying); + var isSearchAllowed = string.IsNullOrEmpty(treeView.searchString) || !(collectingSamples && ProfilerDriver.deepProfiling); - var isDataAvailable = frameDataView != null && frameDataView.IsValid(); - if (isDataAvailable && isSearchAllowed) - if (isDataAvailable) - m_TreeView.SetFrameDataView(frameDataView); + var isDataAvailable = frameDataView != null && frameDataView.IsValid(); + if (isDataAvailable && isSearchAllowed) + if (isDataAvailable) + m_TreeView.SetFrameDataView(frameDataView); - var showDetailedView = isDataAvailable && m_DetailedViewType != DetailedViewType.None; - if (showDetailedView) - SplitterGUILayout.BeginHorizontalSplit(m_DetailedViewSpliterState); + var showDetailedView = isDataAvailable && m_DetailedViewType != DetailedViewType.None; + if (showDetailedView) + SplitterGUILayout.BeginHorizontalSplit(m_DetailedViewSpliterState); - // Hierarchy view area - GUILayout.BeginVertical(); + // Hierarchy view area + GUILayout.BeginVertical(); - DrawToolbar(frameDataView, showDetailedView); + DrawToolbar(frameDataView, showDetailedView); - if (!isDataAvailable) - { - GUILayout.Label(BaseStyles.noData, BaseStyles.label); - } - else if (!isSearchAllowed) - { - GUILayout.Label(BaseStyles.disabledSearchText, BaseStyles.label); - } - else - { - var rect = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.ExpandHeight(true), GUILayout.ExpandHeight(true)); - m_TreeView.OnGUI(rect); - } + if (!isDataAvailable) + { + GUILayout.Label(BaseStyles.noData, BaseStyles.label); + } + else if (!isSearchAllowed) + { + GUILayout.Label(BaseStyles.disabledSearchText, BaseStyles.label); + } + else + { + var rect = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.ExpandHeight(true), GUILayout.ExpandHeight(true)); + m_TreeView.OnGUI(rect); + } - GUILayout.EndVertical(); + GUILayout.EndVertical(); - if (showDetailedView) - { - GUILayout.BeginVertical(); + if (showDetailedView) + { + GUILayout.BeginVertical(); - // Detailed view area - EditorGUILayout.BeginHorizontal(BaseStyles.toolbar); + // Detailed view area + EditorGUILayout.BeginHorizontal(BaseStyles.toolbar); - DrawDetailedViewPopup(); - GUILayout.FlexibleSpace(); + DrawDetailedViewPopup(); + GUILayout.FlexibleSpace(); - DrawOptionsMenuPopup(); - EditorGUILayout.EndHorizontal(); + DrawOptionsMenuPopup(); + EditorGUILayout.EndHorizontal(); - switch (m_DetailedViewType) - { - case DetailedViewType.Objects: - detailedObjectsView.DoGUI(BaseStyles.header, frameDataView, m_TreeView.GetSelection()); - break; - case DetailedViewType.CallersAndCallees: - detailedCallsView.DoGUI(BaseStyles.header, frameDataView, m_TreeView.GetSelection()); - break; - } + switch (m_DetailedViewType) + { + case DetailedViewType.Objects: + detailedObjectsView.DoGUI(BaseStyles.header, frameDataView, m_TreeView.GetSelection()); + break; + case DetailedViewType.CallersAndCallees: + detailedCallsView.DoGUI(BaseStyles.header, frameDataView, m_TreeView.GetSelection()); + break; + } - GUILayout.EndVertical(); + GUILayout.EndVertical(); - SplitterGUILayout.EndHorizontalSplit(); - } + SplitterGUILayout.EndHorizontalSplit(); + } - HandleKeyboardEvents(); + HandleKeyboardEvents(); + } } void DrawSearchBar() diff --git a/Modules/ProfilerEditor/ProfilerWindow/ProfilerTimelineGUI.cs b/Modules/ProfilerEditor/ProfilerWindow/ProfilerTimelineGUI.cs index 9d60438ccd..2bfc0cb031 100644 --- a/Modules/ProfilerEditor/ProfilerWindow/ProfilerTimelineGUI.cs +++ b/Modules/ProfilerEditor/ProfilerWindow/ProfilerTimelineGUI.cs @@ -9,6 +9,7 @@ using UnityEditorInternal.Profiling; using Object = UnityEngine.Object; using UnityEditor.AnimatedValues; +using Unity.Profiling; namespace UnityEditorInternal { @@ -36,12 +37,13 @@ internal class ThreadInfo public int threadIndex; public string name; public bool alive; - public int maxDepth = 1; - public ThreadInfo(string name, int threadIndex, int linesToDisplay) + public int maxDepth; + public ThreadInfo(string name, int threadIndex, int maxDepth, int linesToDisplay) { this.name = name; this.threadIndex = threadIndex; this.linesToDisplay = linesToDisplay; + this.maxDepth = Mathf.Max(1, maxDepth); } } @@ -227,6 +229,8 @@ struct RangeSelectionInfo RangeSelectionInfo m_RangeSelection = new RangeSelectionInfo(); + static readonly ProfilerMarker m_DoGUIMarker = new ProfilerMarker(nameof(ProfilerTimelineGUI) + ".DoGUI"); + public ProfilerTimelineGUI(IProfilerWindowController window) { m_Window = window; @@ -265,43 +269,21 @@ private void UpdateGroupAndThreadInfo(ref ProfilerFrameDataIterator iter, int fr ThreadInfo thread = threads.Find(t => t.threadIndex == i); if (thread == null) { - thread = new ThreadInfo(iter.GetThreadName(), i, group.defaultLineCountPerThread); + // ProfilerFrameDataIterator.maxDepth includes the thread sample which is not getting displayed, so we store it at -1 for all intents and purposes + thread = new ThreadInfo(iter.GetThreadName(), i, iter.maxDepth - 1, group.defaultLineCountPerThread); // the main thread gets double the size if (i == 0) thread.linesToDisplay *= 2; group.threads.Add(thread); } - thread.alive = true; - } - - if (m_LastSelectedFrameID != frameIndex) - { - foreach (var group in m_Groups) - { - foreach (var thread in group.threads) - { - thread.maxDepth = GetThreadMaxDepth(thread, frameIndex); - } - } - m_LastSelectedFrameID = frameIndex; - } - } - - int GetThreadMaxDepth(ThreadInfo thread, int frame) - { - int maxDepth = 1; - using (var iter = new ProfilerFrameDataIterator()) - { - iter.SetRoot(frame, thread.threadIndex); - - while (iter.Next(true)) + else if (m_LastSelectedFrameID != frameIndex) { - if (iter.depth > maxDepth) - maxDepth = iter.depth; + thread.maxDepth = iter.maxDepth; } + thread.alive = true; } - return maxDepth; + m_LastSelectedFrameID = frameIndex; } private float CalculateHeightForAllBars(Rect fullRect, out float combinedHeaderHeight, out float combinedThreadHeight) @@ -702,19 +684,24 @@ void DoProfilerFrame(int frameIndex, Rect fullRect, bool ghost, int threadCount, var groupThreadCount = groupInfo.threads.Count; foreach (var threadInfo in groupInfo.threads) { - iter.SetRoot(frameIndex, threadInfo.threadIndex); r.y = y; r.height = expanded ? threadInfo.height * scaleForThreadHeight : Math.Max(groupInfo.height / groupThreadCount, k_ThreadMinHeightCollapsed); - DoNativeProfilerTimeline(r, frameIndex, threadInfo.threadIndex, offset, ghost, scaleForThreadHeight); - - // Save the y pos and height of the selected thread each time we draw, since it can change - bool containsSelected = m_SelectedEntry.IsValid() && (m_SelectedEntry.frameId == frameIndex) && (m_SelectedEntry.threadId == threadInfo.threadIndex); - if (containsSelected) + var tr = r; + tr.y -= fullRect.y; + if (tr.yMin < m_TimeArea.shownArea.yMax && tr.yMax > m_TimeArea.shownArea.yMin) { - m_SelectedThreadY = y; - m_SelectedThreadYRange = rangeY; - m_SelectedThread = threadInfo; + iter.SetRoot(frameIndex, threadInfo.threadIndex); + DoNativeProfilerTimeline(r, frameIndex, threadInfo.threadIndex, offset, ghost, scaleForThreadHeight); + + // Save the y pos and height of the selected thread each time we draw, since it can change + bool containsSelected = m_SelectedEntry.IsValid() && (m_SelectedEntry.frameId == frameIndex) && (m_SelectedEntry.threadId == threadInfo.threadIndex); + if (containsSelected) + { + m_SelectedThreadY = y; + m_SelectedThreadYRange = rangeY; + m_SelectedThread = threadInfo; + } } y += r.height; @@ -1190,200 +1177,203 @@ void HandleThreadSplitterFoldoutButtons(GroupInfo group, ThreadInfo thread, Rect public void DoGUI(FrameDataView frameDataView, float width, float ypos, float height) { - if (frameDataView == null || !frameDataView.IsValid()) + using (m_DoGUIMarker.Auto()) { - GUILayout.Label(BaseStyles.noData, BaseStyles.label); - return; - } - - Rect fullRect = new Rect(0, ypos - 1, width, height + 1); - float sideWidth = Chart.kSideWidth - 1; + if (frameDataView == null || !frameDataView.IsValid()) + { + GUILayout.Label(BaseStyles.noData, BaseStyles.label); + return; + } - Rect timeRulerRect = new Rect(fullRect.x + sideWidth, fullRect.y, fullRect.width - sideWidth, k_LineHeight); + Rect fullRect = new Rect(0, ypos - 1, width, height + 1); + float sideWidth = Chart.kSideWidth - 1; - Rect timeAreaRect = new Rect(fullRect.x + sideWidth, fullRect.y + timeRulerRect.height, fullRect.width - sideWidth, fullRect.height - timeRulerRect.height); + Rect timeRulerRect = new Rect(fullRect.x + sideWidth, fullRect.y, fullRect.width - sideWidth, k_LineHeight); - bool initializing = false; - if (m_TimeArea == null) - { - initializing = true; - m_TimeArea = new ZoomableArea(); - m_TimeArea.hRangeLocked = false; - m_TimeArea.vRangeLocked = false; - m_TimeArea.hSlider = true; - m_TimeArea.vSlider = true; - m_TimeArea.vAllowExceedBaseRangeMax = false; - m_TimeArea.vAllowExceedBaseRangeMin = false; - m_TimeArea.hBaseRangeMin = 0; - m_TimeArea.vBaseRangeMin = 0; - m_TimeArea.vScaleMax = 1f; - m_TimeArea.vScaleMin = 1f; - m_TimeArea.scaleWithWindow = true; - m_TimeArea.margin = 10; - m_TimeArea.topmargin = 0; - m_TimeArea.bottommargin = 0; - m_TimeArea.upDirection = ZoomableArea.YDirection.Negative; - m_TimeArea.vZoomLockedByDefault = true; - } + Rect timeAreaRect = new Rect(fullRect.x + sideWidth, fullRect.y + timeRulerRect.height, fullRect.width - sideWidth, fullRect.height - timeRulerRect.height); - m_TimeArea.rect = timeAreaRect; + bool initializing = false; + if (m_TimeArea == null) + { + initializing = true; + m_TimeArea = new ZoomableArea(); + m_TimeArea.hRangeLocked = false; + m_TimeArea.vRangeLocked = false; + m_TimeArea.hSlider = true; + m_TimeArea.vSlider = true; + m_TimeArea.vAllowExceedBaseRangeMax = false; + m_TimeArea.vAllowExceedBaseRangeMin = false; + m_TimeArea.hBaseRangeMin = 0; + m_TimeArea.vBaseRangeMin = 0; + m_TimeArea.vScaleMax = 1f; + m_TimeArea.vScaleMin = 1f; + m_TimeArea.scaleWithWindow = true; + m_TimeArea.margin = 10; + m_TimeArea.topmargin = 0; + m_TimeArea.bottommargin = 0; + m_TimeArea.upDirection = ZoomableArea.YDirection.Negative; + m_TimeArea.vZoomLockedByDefault = true; + } - Rect bottomLeftFillRect = new Rect(0, ypos + height - m_TimeArea.vSliderWidth, sideWidth, m_TimeArea.vSliderWidth); + m_TimeArea.rect = timeAreaRect; - if (Event.current.type == EventType.Repaint) - { - styles.profilerGraphBackground.Draw(fullRect, false, false, false, false); - // The bar in the lower left side that fills the space next to the horizontal scrollbar. - EditorStyles.toolbar.Draw(bottomLeftFillRect, false, false, false, false); - } + Rect bottomLeftFillRect = new Rect(0, ypos + height - m_TimeArea.vSliderWidth, sideWidth, m_TimeArea.vSliderWidth); - if (initializing) - { - NativeProfilerTimeline_InitializeArgs args = new NativeProfilerTimeline_InitializeArgs(); - args.Reset(); - args.ghostAlpha = 0.3f; - args.nonSelectedAlpha = 0.75f; - args.guiStyle = styles.bar.m_Ptr; - args.lineHeight = k_LineHeight; - args.textFadeOutWidth = k_TextFadeOutWidth; - args.textFadeStartWidth = k_TextFadeStartWidth; - - NativeProfilerTimeline.Initialize(ref args); - } - // Prepare group and Thread Info - var iter = new ProfilerFrameDataIterator(); - int threadCount = iter.GetThreadCount(frameDataView.frameIndex); - iter.SetRoot(frameDataView.frameIndex, 0); - UpdateGroupAndThreadInfo(ref iter, frameDataView.frameIndex); - MarkDeadOrClearThread(); + if (Event.current.type == EventType.Repaint) + { + styles.profilerGraphBackground.Draw(fullRect, false, false, false, false); + // The bar in the lower left side that fills the space next to the horizontal scrollbar. + EditorStyles.toolbar.Draw(bottomLeftFillRect, false, false, false, false); + } - HandleFrameSelected(iter.frameTimeMS); + if (initializing) + { + NativeProfilerTimeline_InitializeArgs args = new NativeProfilerTimeline_InitializeArgs(); + args.Reset(); + args.ghostAlpha = 0.3f; + args.nonSelectedAlpha = 0.75f; + args.guiStyle = styles.bar.m_Ptr; + args.lineHeight = k_LineHeight; + args.textFadeOutWidth = k_TextFadeOutWidth; + args.textFadeStartWidth = k_TextFadeStartWidth; + + NativeProfilerTimeline.Initialize(ref args); + } + // Prepare group and Thread Info + var iter = new ProfilerFrameDataIterator(); + int threadCount = iter.GetThreadCount(frameDataView.frameIndex); + iter.SetRoot(frameDataView.frameIndex, 0); + UpdateGroupAndThreadInfo(ref iter, frameDataView.frameIndex); + MarkDeadOrClearThread(); - // update time area to new bounds - float combinedHeaderHeight, combinedThreadHeight; - float heightForAllBars = CalculateHeightForAllBars(fullRect, out combinedHeaderHeight, out combinedThreadHeight); - float emptySpaceBelowBars = k_LineHeight * 3f; + HandleFrameSelected(iter.frameTimeMS); - // if needed, take up more empty space below, to fill up the ZoomableArea - emptySpaceBelowBars = Mathf.Max(emptySpaceBelowBars, timeAreaRect.height - heightForAllBars); + // update time area to new bounds + float combinedHeaderHeight, combinedThreadHeight; + float heightForAllBars = CalculateHeightForAllBars(fullRect, out combinedHeaderHeight, out combinedThreadHeight); + float emptySpaceBelowBars = k_LineHeight * 3f; - heightForAllBars += emptySpaceBelowBars; + // if needed, take up more empty space below, to fill up the ZoomableArea + emptySpaceBelowBars = Mathf.Max(emptySpaceBelowBars, timeAreaRect.height - heightForAllBars); - m_TimeArea.hBaseRangeMax = iter.frameTimeMS; - m_TimeArea.vBaseRangeMax = heightForAllBars; + heightForAllBars += emptySpaceBelowBars; - if (Mathf.Abs(heightForAllBars - m_LastHeightForAllBars) >= 0.5f || Mathf.Abs(fullRect.height - m_LastFullRectHeight) >= 0.5f) - { - m_LastHeightForAllBars = heightForAllBars; - m_LastFullRectHeight = fullRect.height; + m_TimeArea.hBaseRangeMax = iter.frameTimeMS; + m_TimeArea.vBaseRangeMax = heightForAllBars; - // set V range to enforce scale of 1 and to shift the shown area up in case the drawn area shrunk down - m_TimeArea.SetShownVRange(m_TimeArea.shownArea.y, m_TimeArea.shownArea.y + m_TimeArea.drawRect.height); - } + if (Mathf.Abs(heightForAllBars - m_LastHeightForAllBars) >= 0.5f || Mathf.Abs(fullRect.height - m_LastFullRectHeight) >= 0.5f) + { + m_LastHeightForAllBars = heightForAllBars; + m_LastFullRectHeight = fullRect.height; - // frame the selection if needed and before drawing the time area - if (initializing) - PerformFrameSelected(iter.frameTimeMS); + // set V range to enforce scale of 1 and to shift the shown area up in case the drawn area shrunk down + m_TimeArea.SetShownVRange(m_TimeArea.shownArea.y, m_TimeArea.shownArea.y + m_TimeArea.drawRect.height); + } - DoTimeRulerGUI(timeRulerRect, sideWidth, iter.frameTimeMS); - DoTimeArea(); + // frame the selection if needed and before drawing the time area + if (initializing) + PerformFrameSelected(iter.frameTimeMS); - Rect fullThreadsRect = new Rect(fullRect.x, fullRect.y + timeRulerRect.height, fullRect.width - m_TimeArea.vSliderWidth, fullRect.height - timeRulerRect.height - m_TimeArea.hSliderHeight); + DoTimeRulerGUI(timeRulerRect, sideWidth, iter.frameTimeMS); + DoTimeArea(); - Rect fullThreadsRectWithoutSidebar = fullThreadsRect; - fullThreadsRectWithoutSidebar.x += sideWidth; - fullThreadsRectWithoutSidebar.width -= sideWidth; + Rect fullThreadsRect = new Rect(fullRect.x, fullRect.y + timeRulerRect.height, fullRect.width - m_TimeArea.vSliderWidth, fullRect.height - timeRulerRect.height - m_TimeArea.hSliderHeight); - // The splitters need to be handled after the time area so that they don't interfere with the input for panning/scrolling the ZoomableArea - DoThreadSplitters(fullThreadsRect, fullThreadsRectWithoutSidebar, frameDataView.frameIndex, ThreadSplitterCommand.HandleThreadSplitter); + Rect fullThreadsRectWithoutSidebar = fullThreadsRect; + fullThreadsRectWithoutSidebar.x += sideWidth; + fullThreadsRectWithoutSidebar.width -= sideWidth; - Rect barsUIRect = m_TimeArea.drawRect; + // The splitters need to be handled after the time area so that they don't interfere with the input for panning/scrolling the ZoomableArea + DoThreadSplitters(fullThreadsRect, fullThreadsRectWithoutSidebar, frameDataView.frameIndex, ThreadSplitterCommand.HandleThreadSplitter); - DrawGrid(barsUIRect, iter.frameTimeMS); + Rect barsUIRect = m_TimeArea.drawRect; - Rect barsAndSidebarUIRect = new Rect(barsUIRect.x - sideWidth, barsUIRect.y, barsUIRect.width + sideWidth, barsUIRect.height); + DrawGrid(barsUIRect, iter.frameTimeMS); - GUI.BeginClip(barsAndSidebarUIRect); + Rect barsAndSidebarUIRect = new Rect(barsUIRect.x - sideWidth, barsUIRect.y, barsUIRect.width + sideWidth, barsUIRect.height); - Rect shownBarsUIRect = barsUIRect; - shownBarsUIRect.y = scrollOffsetY; + GUI.BeginClip(barsAndSidebarUIRect); - // since the scale is not applied to the group headers, there would be some height unaccounted for - // this calculation applies that height to the threads via scaleForThreadHeight - float heightUnaccountedForDueToNotScalingHeaders = m_TimeArea.scale.y * combinedHeaderHeight - combinedHeaderHeight; - float scaleForThreadHeight = (combinedThreadHeight * m_TimeArea.scale.y + heightUnaccountedForDueToNotScalingHeaders) / combinedThreadHeight; + Rect shownBarsUIRect = barsUIRect; + shownBarsUIRect.y = scrollOffsetY; - DrawBars(shownBarsUIRect, scaleForThreadHeight); - GUI.EndClip(); + // since the scale is not applied to the group headers, there would be some height unaccounted for + // this calculation applies that height to the threads via scaleForThreadHeight + float heightUnaccountedForDueToNotScalingHeaders = m_TimeArea.scale.y * combinedHeaderHeight - combinedHeaderHeight; + float scaleForThreadHeight = (combinedThreadHeight * m_TimeArea.scale.y + heightUnaccountedForDueToNotScalingHeaders) / combinedThreadHeight; - DoRangeSelection(barsUIRect); + DrawBars(shownBarsUIRect, scaleForThreadHeight); + GUI.EndClip(); - GUI.BeginClip(barsUIRect); - shownBarsUIRect.x = 0; + DoRangeSelection(barsUIRect); - bool oldEnabled = GUI.enabled; - GUI.enabled = false; - // Walk backwards to find how many previous frames we need to show. - int maxContextFramesToShow = m_Window.IsRecording() ? 1 : 3; - int numContextFramesToShow = maxContextFramesToShow; - int currentFrame = frameDataView.frameIndex; - float currentTime = 0; - do - { - int prevFrame = ProfilerDriver.GetPreviousFrameIndex(currentFrame); - if (prevFrame == -1) - break; - iter.SetRoot(prevFrame, 0); - currentTime -= iter.frameTimeMS; - currentFrame = prevFrame; - --numContextFramesToShow; - } - while (currentTime > m_TimeArea.shownArea.x && numContextFramesToShow > 0); + GUI.BeginClip(barsUIRect); + shownBarsUIRect.x = 0; - // Draw previous frames - while (currentFrame != -1 && currentFrame != frameDataView.frameIndex) - { - iter.SetRoot(currentFrame, 0); - DoProfilerFrame(currentFrame, shownBarsUIRect, true, threadCount, currentTime, scaleForThreadHeight); - currentTime += iter.frameTimeMS; - currentFrame = ProfilerDriver.GetNextFrameIndex(currentFrame); - } + bool oldEnabled = GUI.enabled; + GUI.enabled = false; + // Walk backwards to find how many previous frames we need to show. + int maxContextFramesToShow = m_Window.IsRecording() ? 1 : 3; + int numContextFramesToShow = maxContextFramesToShow; + int currentFrame = frameDataView.frameIndex; + float currentTime = 0; + do + { + int prevFrame = ProfilerDriver.GetPreviousFrameIndex(currentFrame); + if (prevFrame == -1) + break; + iter.SetRoot(prevFrame, 0); + currentTime -= iter.frameTimeMS; + currentFrame = prevFrame; + --numContextFramesToShow; + } + while (currentTime > m_TimeArea.shownArea.x && numContextFramesToShow > 0); - // Draw next frames - numContextFramesToShow = maxContextFramesToShow; - currentFrame = frameDataView.frameIndex; - currentTime = 0; - while (currentTime < m_TimeArea.shownArea.x + m_TimeArea.shownArea.width && numContextFramesToShow >= 0) - { - if (frameDataView.frameIndex != currentFrame) + // Draw previous frames + while (currentFrame != -1 && currentFrame != frameDataView.frameIndex) + { + iter.SetRoot(currentFrame, 0); DoProfilerFrame(currentFrame, shownBarsUIRect, true, threadCount, currentTime, scaleForThreadHeight); - iter.SetRoot(currentFrame, 0); - currentFrame = ProfilerDriver.GetNextFrameIndex(currentFrame); - if (currentFrame == -1) - break; - currentTime += iter.frameTimeMS; - --numContextFramesToShow; - } + currentTime += iter.frameTimeMS; + currentFrame = ProfilerDriver.GetNextFrameIndex(currentFrame); + } - GUI.enabled = oldEnabled; + // Draw next frames + numContextFramesToShow = maxContextFramesToShow; + currentFrame = frameDataView.frameIndex; + currentTime = 0; + while (currentTime < m_TimeArea.shownArea.x + m_TimeArea.shownArea.width && numContextFramesToShow >= 0) + { + if (frameDataView.frameIndex != currentFrame) + DoProfilerFrame(currentFrame, shownBarsUIRect, true, threadCount, currentTime, scaleForThreadHeight); + iter.SetRoot(currentFrame, 0); + currentFrame = ProfilerDriver.GetNextFrameIndex(currentFrame); + if (currentFrame == -1) + break; + currentTime += iter.frameTimeMS; + --numContextFramesToShow; + } - // Draw center frame last to get on top - threadCount = 0; - DoProfilerFrame(frameDataView.frameIndex, shownBarsUIRect, false, threadCount, 0, scaleForThreadHeight); + GUI.enabled = oldEnabled; - GUI.EndClip(); + // Draw center frame last to get on top + threadCount = 0; + DoProfilerFrame(frameDataView.frameIndex, shownBarsUIRect, false, threadCount, 0, scaleForThreadHeight); - // Draw Foldout Buttons on top of natively drawn bars - DoThreadSplitters(fullThreadsRect, fullThreadsRectWithoutSidebar, frameDataView.frameIndex, ThreadSplitterCommand.HandleThreadSplitterFoldoutButtons); + GUI.EndClip(); - // Draw tooltips on top of clip to be able to extend outside of timeline area - DoSelectionTooltip(frameDataView.frameIndex, m_TimeArea.drawRect); + // Draw Foldout Buttons on top of natively drawn bars + DoThreadSplitters(fullThreadsRect, fullThreadsRectWithoutSidebar, frameDataView.frameIndex, ThreadSplitterCommand.HandleThreadSplitterFoldoutButtons); - if (Event.current.type == EventType.Repaint) - { - // Reset all flags once Repaint finished on this view - m_LastRepaintProcessedInputs = m_CurrentlyProcessedInputs; - m_CurrentlyProcessedInputs = 0; + // Draw tooltips on top of clip to be able to extend outside of timeline area + DoSelectionTooltip(frameDataView.frameIndex, m_TimeArea.drawRect); + + if (Event.current.type == EventType.Repaint) + { + // Reset all flags once Repaint finished on this view + m_LastRepaintProcessedInputs = m_CurrentlyProcessedInputs; + m_CurrentlyProcessedInputs = 0; + } } } diff --git a/Modules/ProfilerEditor/ProfilerWindow/ProfilerWindow.cs b/Modules/ProfilerEditor/ProfilerWindow/ProfilerWindow.cs index 1228f2fe70..127ff30dbb 100644 --- a/Modules/ProfilerEditor/ProfilerWindow/ProfilerWindow.cs +++ b/Modules/ProfilerEditor/ProfilerWindow/ProfilerWindow.cs @@ -107,7 +107,8 @@ static Styles() private Vector2 m_GraphPos = Vector2.zero; private Vector2[] m_PaneScroll = new Vector2[Profiler.areaCount]; private Vector2 m_PaneScroll_AudioChannels = Vector2.zero; - private Vector2 m_PaneScroll_AudioDSP = Vector2.zero; + private Vector2 m_PaneScroll_AudioDSPLeft = Vector2.zero; + private Vector2 m_PaneScroll_AudioDSPRight = Vector2.zero; private Vector2 m_PaneScroll_AudioClips = Vector2.zero; [SerializeField] @@ -122,7 +123,7 @@ static Styles() ProfilerArea? m_CurrentArea = ProfilerArea.CPU; ProfilerMemoryView m_ShowDetailedMemoryPane = ProfilerMemoryView.Simple; - ProfilerAudioView m_ShowDetailedAudioPane = ProfilerAudioView.Stats; + ProfilerAudioView m_ShowDetailedAudioPane = ProfilerAudioView.Channels; [SerializeField] bool m_ShowInactiveDSPChains = false; @@ -837,194 +838,192 @@ void DrawNetworkOperationsPane() } - private void AudioProfilerToggle(ProfilerCaptureFlags toggleFlag) + private enum ProfilerAudioPopupItems { - bool oldState = (AudioSettings.profilerCaptureFlags & (int)toggleFlag) != 0; - bool newState = GUILayout.Toggle(oldState, "Record", EditorStyles.toolbarButton); - if (oldState != newState) - ProfilerDriver.SetAudioCaptureFlags((AudioSettings.profilerCaptureFlags & ~(int)toggleFlag) | (newState - ? (int)toggleFlag - : 0)); + Simple = 0, + Detailed = 1 + } + + private bool AudioDeepProfileToggle() + { + int toggleFlags = (int)ProfilerCaptureFlags.Channels; + if (Unsupported.IsDeveloperMode()) + toggleFlags |= (int)ProfilerCaptureFlags.Clips | (int)ProfilerCaptureFlags.DSPNodes; + ProfilerAudioPopupItems oldShowDetailedAudioPane = (AudioSettings.profilerCaptureFlags & toggleFlags) != 0 ? ProfilerAudioPopupItems.Detailed : ProfilerAudioPopupItems.Simple; + ProfilerAudioPopupItems newShowDetailedAudioPane = (ProfilerAudioPopupItems)EditorGUILayout.EnumPopup(oldShowDetailedAudioPane, EditorStyles.toolbarDropDown, GUILayout.Width(70f)); + if (oldShowDetailedAudioPane != newShowDetailedAudioPane) + ProfilerDriver.SetAudioCaptureFlags((AudioSettings.profilerCaptureFlags & ~toggleFlags) | (newShowDetailedAudioPane == ProfilerAudioPopupItems.Detailed ? toggleFlags : 0)); + return (AudioSettings.profilerCaptureFlags & toggleFlags) != 0; + } + + private Rect DrawAudioStatsPane(ref Vector2 scrollPos) + { + var totalRect = GUILayoutUtility.GetRect(20f, 20000f, 10, 10000f); + var statsRect = new Rect(totalRect.x, totalRect.y, 230f, totalRect.height); + var rightRect = new Rect(statsRect.xMax, totalRect.y, totalRect.width - statsRect.width, totalRect.height); + + // STATS + var content = ProfilerDriver.GetOverviewText(m_CurrentArea.Value, GetActiveVisibleFrameIndex()); + var textSize = EditorStyles.wordWrappedLabel.CalcSize(GUIContent.Temp(content)); + scrollPos = GUI.BeginScrollView(statsRect, scrollPos, new Rect(0, 0, textSize.x, textSize.y)); + GUI.Label(new Rect(3, 3, textSize.x, textSize.y), content, EditorStyles.wordWrappedLabel); + GUI.EndScrollView(); + EditorGUI.DrawRect(new Rect(statsRect.xMax - 1, statsRect.y, 1, statsRect.height), Color.black); + + return rightRect; } private void DrawAudioPane() { EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); ProfilerAudioView newShowDetailedAudioPane = m_ShowDetailedAudioPane; - if (GUILayout.Toggle(newShowDetailedAudioPane == ProfilerAudioView.Stats, "Stats", EditorStyles.toolbarButton)) newShowDetailedAudioPane = ProfilerAudioView.Stats; - if (GUILayout.Toggle(newShowDetailedAudioPane == ProfilerAudioView.Channels, "Channels", EditorStyles.toolbarButton)) newShowDetailedAudioPane = ProfilerAudioView.Channels; - if (GUILayout.Toggle(newShowDetailedAudioPane == ProfilerAudioView.Groups, "Groups", EditorStyles.toolbarButton)) newShowDetailedAudioPane = ProfilerAudioView.Groups; - if (GUILayout.Toggle(newShowDetailedAudioPane == ProfilerAudioView.ChannelsAndGroups, "Channels and groups", EditorStyles.toolbarButton)) newShowDetailedAudioPane = ProfilerAudioView.ChannelsAndGroups; - if (Unsupported.IsDeveloperMode() && GUILayout.Toggle(newShowDetailedAudioPane == ProfilerAudioView.DSPGraph, "DSP Graph", EditorStyles.toolbarButton)) newShowDetailedAudioPane = ProfilerAudioView.DSPGraph; - if (Unsupported.IsDeveloperMode() && GUILayout.Toggle(newShowDetailedAudioPane == ProfilerAudioView.Clips, "Clips", EditorStyles.toolbarButton)) newShowDetailedAudioPane = ProfilerAudioView.Clips; - if (newShowDetailedAudioPane != m_ShowDetailedAudioPane) - { - m_ShowDetailedAudioPane = newShowDetailedAudioPane; - m_LastAudioProfilerFrame = -1; // force update - } - if (m_ShowDetailedAudioPane == ProfilerAudioView.Stats) - { - GUILayout.Space(5); - GUILayout.FlexibleSpace(); - EditorGUILayout.EndHorizontal(); - DrawOverviewText(m_CurrentArea); - } - else if (m_ShowDetailedAudioPane == ProfilerAudioView.DSPGraph) + if (AudioDeepProfileToggle()) { - GUILayout.Space(5); - AudioProfilerToggle(ProfilerCaptureFlags.DSPNodes); - GUILayout.Space(5); - m_ShowInactiveDSPChains = GUILayout.Toggle(m_ShowInactiveDSPChains, "Show inactive", EditorStyles.toolbarButton); - if (m_ShowInactiveDSPChains) - m_HighlightAudibleDSPChains = GUILayout.Toggle(m_HighlightAudibleDSPChains, "Highlight audible", EditorStyles.toolbarButton); - GUILayout.FlexibleSpace(); - EditorGUILayout.EndHorizontal(); + if (GUILayout.Toggle(newShowDetailedAudioPane == ProfilerAudioView.Channels, "Channels", EditorStyles.toolbarButton)) newShowDetailedAudioPane = ProfilerAudioView.Channels; + if (GUILayout.Toggle(newShowDetailedAudioPane == ProfilerAudioView.Groups, "Groups", EditorStyles.toolbarButton)) newShowDetailedAudioPane = ProfilerAudioView.Groups; + if (GUILayout.Toggle(newShowDetailedAudioPane == ProfilerAudioView.ChannelsAndGroups, "Channels and groups", EditorStyles.toolbarButton)) newShowDetailedAudioPane = ProfilerAudioView.ChannelsAndGroups; + if (Unsupported.IsDeveloperMode() && GUILayout.Toggle(newShowDetailedAudioPane == ProfilerAudioView.DSPGraph, "DSP Graph", EditorStyles.toolbarButton)) newShowDetailedAudioPane = ProfilerAudioView.DSPGraph; + if (Unsupported.IsDeveloperMode() && GUILayout.Toggle(newShowDetailedAudioPane == ProfilerAudioView.Clips, "Clips", EditorStyles.toolbarButton)) newShowDetailedAudioPane = ProfilerAudioView.Clips; + if (newShowDetailedAudioPane != m_ShowDetailedAudioPane) + { + m_ShowDetailedAudioPane = newShowDetailedAudioPane; + m_LastAudioProfilerFrame = -1; // force update + } + if (m_ShowDetailedAudioPane == ProfilerAudioView.DSPGraph) + { + m_ShowInactiveDSPChains = GUILayout.Toggle(m_ShowInactiveDSPChains, "Show inactive", EditorStyles.toolbarButton); + if (m_ShowInactiveDSPChains) + m_HighlightAudibleDSPChains = GUILayout.Toggle(m_HighlightAudibleDSPChains, "Highlight audible", EditorStyles.toolbarButton); + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); - var totalRect = GUILayoutUtility.GetRect(20f, 10000f, 10, 20000f); + var graphRect = DrawAudioStatsPane(ref m_PaneScroll_AudioDSPLeft); - m_PaneScroll_AudioDSP = GUI.BeginScrollView(totalRect, m_PaneScroll_AudioDSP, new Rect(0, 0, 10000, 20000)); + m_PaneScroll_AudioDSPRight = GUI.BeginScrollView(graphRect, m_PaneScroll_AudioDSPRight, new Rect(0, 0, 10000, 20000)); - var clippingRect = new Rect(m_PaneScroll_AudioDSP.x, m_PaneScroll_AudioDSP.y, totalRect.width, totalRect.height); + var clippingRect = new Rect(m_PaneScroll_AudioDSPRight.x, m_PaneScroll_AudioDSPRight.y, graphRect.width, graphRect.height); - if (m_AudioProfilerDSPView == null) - m_AudioProfilerDSPView = new AudioProfilerDSPView(); + if (m_AudioProfilerDSPView == null) + m_AudioProfilerDSPView = new AudioProfilerDSPView(); - ProfilerProperty property = CreateProperty(); - if (CheckFrameData(property)) - { - m_AudioProfilerDSPView.OnGUI(clippingRect, property, m_ShowInactiveDSPChains, m_HighlightAudibleDSPChains, ref m_DSPGraphZoomFactor, ref m_PaneScroll_AudioDSP); - } - if (property != null) - property.Dispose(); + ProfilerProperty property = CreateProperty(); + if (CheckFrameData(property)) + { + m_AudioProfilerDSPView.OnGUI(clippingRect, property, m_ShowInactiveDSPChains, m_HighlightAudibleDSPChains, ref m_DSPGraphZoomFactor, ref m_PaneScroll_AudioDSPRight); + } + if (property != null) + property.Dispose(); - GUI.EndScrollView(); + GUI.EndScrollView(); - Repaint(); - } - else if (m_ShowDetailedAudioPane == ProfilerAudioView.Clips) - { - GUILayout.Space(5); - AudioProfilerToggle(ProfilerCaptureFlags.Clips); - GUILayout.FlexibleSpace(); - EditorGUILayout.EndHorizontal(); - - var totalRect = GUILayoutUtility.GetRect(20f, 20000f, 10, 10000f); - var statsRect = new Rect(totalRect.x, totalRect.y, 230f, totalRect.height); - var treeRect = new Rect(statsRect.xMax, totalRect.y, totalRect.width - statsRect.width, totalRect.height); + Repaint(); + } + else if (m_ShowDetailedAudioPane == ProfilerAudioView.Clips) + { + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); - // STATS - var content = ProfilerDriver.GetOverviewText(m_CurrentArea.Value, GetActiveVisibleFrameIndex()); - var textSize = EditorStyles.wordWrappedLabel.CalcSize(GUIContent.Temp(content)); - m_PaneScroll_AudioClips = GUI.BeginScrollView(statsRect, m_PaneScroll_AudioClips, new Rect(0, 0, textSize.x, textSize.y)); - GUI.Label(new Rect(3, 3, textSize.x, textSize.y), content, EditorStyles.wordWrappedLabel); - GUI.EndScrollView(); - EditorGUI.DrawRect(new Rect(statsRect.xMax - 1, statsRect.y, 1, statsRect.height), Color.black); + var treeRect = DrawAudioStatsPane(ref m_PaneScroll_AudioClips); - // TREE - if (m_AudioProfilerClipTreeViewState == null) - m_AudioProfilerClipTreeViewState = new AudioProfilerClipTreeViewState(); + // TREE + if (m_AudioProfilerClipTreeViewState == null) + m_AudioProfilerClipTreeViewState = new AudioProfilerClipTreeViewState(); - if (m_AudioProfilerClipViewBackend == null) - m_AudioProfilerClipViewBackend = new AudioProfilerClipViewBackend(m_AudioProfilerClipTreeViewState); + if (m_AudioProfilerClipViewBackend == null) + m_AudioProfilerClipViewBackend = new AudioProfilerClipViewBackend(m_AudioProfilerClipTreeViewState); - ProfilerProperty property = CreateProperty(); - if (CheckFrameData(property)) - { - if (m_CurrentFrame == -1 || m_LastAudioProfilerFrame != m_CurrentFrame) + ProfilerProperty property = CreateProperty(); + if (CheckFrameData(property)) { - m_LastAudioProfilerFrame = m_CurrentFrame; - var sourceItems = property.GetAudioProfilerClipInfo(); - if (sourceItems != null && sourceItems.Length > 0) + if (m_CurrentFrame == -1 || m_LastAudioProfilerFrame != m_CurrentFrame) { - var items = new List(); - foreach (var s in sourceItems) - { - items.Add(new AudioProfilerClipInfoWrapper(s, property.GetAudioProfilerNameByOffset(s.assetNameOffset))); - } - m_AudioProfilerClipViewBackend.SetData(items); - if (m_AudioProfilerClipView == null) + m_LastAudioProfilerFrame = m_CurrentFrame; + var sourceItems = property.GetAudioProfilerClipInfo(); + if (sourceItems != null && sourceItems.Length > 0) { - m_AudioProfilerClipView = new AudioProfilerClipView(this, m_AudioProfilerClipTreeViewState); - m_AudioProfilerClipView.Init(treeRect, m_AudioProfilerClipViewBackend); + var items = new List(); + foreach (var s in sourceItems) + { + items.Add(new AudioProfilerClipInfoWrapper(s, property.GetAudioProfilerNameByOffset(s.assetNameOffset))); + } + m_AudioProfilerClipViewBackend.SetData(items); + if (m_AudioProfilerClipView == null) + { + m_AudioProfilerClipView = new AudioProfilerClipView(this, m_AudioProfilerClipTreeViewState); + m_AudioProfilerClipView.Init(treeRect, m_AudioProfilerClipViewBackend); + } } } + if (m_AudioProfilerClipView != null) + m_AudioProfilerClipView.OnGUI(treeRect); } - if (m_AudioProfilerClipView != null) - m_AudioProfilerClipView.OnGUI(treeRect); + if (property != null) + property.Dispose(); } - if (property != null) - property.Dispose(); - } - else - { - GUILayout.Space(5); - AudioProfilerToggle(ProfilerCaptureFlags.Channels); - GUILayout.Space(5); - bool resetAllAudioClipPlayCountsOnPlay = GUILayout.Toggle(AudioUtil.resetAllAudioClipPlayCountsOnPlay, "Reset play count on play", EditorStyles.toolbarButton); - if (resetAllAudioClipPlayCountsOnPlay != AudioUtil.resetAllAudioClipPlayCountsOnPlay) - AudioUtil.resetAllAudioClipPlayCountsOnPlay = resetAllAudioClipPlayCountsOnPlay; - if (Unsupported.IsDeveloperMode()) + else { - GUILayout.Space(5); - bool showAllGroups = EditorPrefs.GetBool("AudioProfilerShowAllGroups"); - bool newShowAllGroups = GUILayout.Toggle(showAllGroups, "Show all groups (dev mode only)", EditorStyles.toolbarButton); - if (showAllGroups != newShowAllGroups) - EditorPrefs.SetBool("AudioProfilerShowAllGroups", newShowAllGroups); - } - GUILayout.FlexibleSpace(); - EditorGUILayout.EndHorizontal(); - - var totalRect = GUILayoutUtility.GetRect(20f, 20000f, 10, 10000f); - var statsRect = new Rect(totalRect.x, totalRect.y, 230f, totalRect.height); - var treeRect = new Rect(statsRect.xMax, totalRect.y, totalRect.width - statsRect.width, totalRect.height); + bool resetAllAudioClipPlayCountsOnPlay = GUILayout.Toggle(AudioUtil.resetAllAudioClipPlayCountsOnPlay, "Reset play count on play", EditorStyles.toolbarButton); + if (resetAllAudioClipPlayCountsOnPlay != AudioUtil.resetAllAudioClipPlayCountsOnPlay) + AudioUtil.resetAllAudioClipPlayCountsOnPlay = resetAllAudioClipPlayCountsOnPlay; + if (Unsupported.IsDeveloperMode()) + { + GUILayout.Space(5); + bool showAllGroups = EditorPrefs.GetBool("AudioProfilerShowAllGroups"); + bool newShowAllGroups = GUILayout.Toggle(showAllGroups, "Show all groups (dev mode only)", EditorStyles.toolbarButton); + if (showAllGroups != newShowAllGroups) + EditorPrefs.SetBool("AudioProfilerShowAllGroups", newShowAllGroups); + } + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); - // STATS - var content = ProfilerDriver.GetOverviewText(m_CurrentArea.Value, GetActiveVisibleFrameIndex()); - var textSize = EditorStyles.wordWrappedLabel.CalcSize(GUIContent.Temp(content)); - m_PaneScroll_AudioChannels = GUI.BeginScrollView(statsRect, m_PaneScroll_AudioChannels, new Rect(0, 0, textSize.x, textSize.y)); - GUI.Label(new Rect(3, 3, textSize.x, textSize.y), content, EditorStyles.wordWrappedLabel); - GUI.EndScrollView(); - EditorGUI.DrawRect(new Rect(statsRect.xMax - 1, statsRect.y, 1, statsRect.height), Color.black); + var treeRect = DrawAudioStatsPane(ref m_PaneScroll_AudioChannels); - // TREE - if (m_AudioProfilerGroupTreeViewState == null) - m_AudioProfilerGroupTreeViewState = new AudioProfilerGroupTreeViewState(); + // TREE + if (m_AudioProfilerGroupTreeViewState == null) + m_AudioProfilerGroupTreeViewState = new AudioProfilerGroupTreeViewState(); - if (m_AudioProfilerGroupViewBackend == null) - m_AudioProfilerGroupViewBackend = new AudioProfilerGroupViewBackend(m_AudioProfilerGroupTreeViewState); + if (m_AudioProfilerGroupViewBackend == null) + m_AudioProfilerGroupViewBackend = new AudioProfilerGroupViewBackend(m_AudioProfilerGroupTreeViewState); - ProfilerProperty property = CreateProperty(); - if (CheckFrameData(property)) - { - if (m_CurrentFrame == -1 || m_LastAudioProfilerFrame != m_CurrentFrame) + ProfilerProperty property = CreateProperty(); + if (CheckFrameData(property)) { - m_LastAudioProfilerFrame = m_CurrentFrame; - var sourceItems = property.GetAudioProfilerGroupInfo(); - if (sourceItems != null && sourceItems.Length > 0) + if (m_CurrentFrame == -1 || m_LastAudioProfilerFrame != m_CurrentFrame) { - var items = new List(); - foreach (var s in sourceItems) - { - bool isGroup = (s.flags & AudioProfilerGroupInfoHelper.AUDIOPROFILER_FLAGS_GROUP) != 0; - if (m_ShowDetailedAudioPane == ProfilerAudioView.Channels && isGroup) - continue; - if (m_ShowDetailedAudioPane == ProfilerAudioView.Groups && !isGroup) - continue; - items.Add(new AudioProfilerGroupInfoWrapper(s, property.GetAudioProfilerNameByOffset(s.assetNameOffset), property.GetAudioProfilerNameByOffset(s.objectNameOffset), m_ShowDetailedAudioPane == ProfilerAudioView.Channels)); - } - m_AudioProfilerGroupViewBackend.SetData(items); - if (m_AudioProfilerGroupView == null) + m_LastAudioProfilerFrame = m_CurrentFrame; + var sourceItems = property.GetAudioProfilerGroupInfo(); + if (sourceItems != null && sourceItems.Length > 0) { - m_AudioProfilerGroupView = new AudioProfilerGroupView(this, m_AudioProfilerGroupTreeViewState); - m_AudioProfilerGroupView.Init(treeRect, m_AudioProfilerGroupViewBackend); + var items = new List(); + foreach (var s in sourceItems) + { + bool isGroup = (s.flags & AudioProfilerGroupInfoHelper.AUDIOPROFILER_FLAGS_GROUP) != 0; + if (m_ShowDetailedAudioPane == ProfilerAudioView.Channels && isGroup) + continue; + if (m_ShowDetailedAudioPane == ProfilerAudioView.Groups && !isGroup) + continue; + items.Add(new AudioProfilerGroupInfoWrapper(s, property.GetAudioProfilerNameByOffset(s.assetNameOffset), property.GetAudioProfilerNameByOffset(s.objectNameOffset), m_ShowDetailedAudioPane == ProfilerAudioView.Channels)); + } + m_AudioProfilerGroupViewBackend.SetData(items); + if (m_AudioProfilerGroupView == null) + { + m_AudioProfilerGroupView = new AudioProfilerGroupView(this, m_AudioProfilerGroupTreeViewState); + m_AudioProfilerGroupView.Init(treeRect, m_AudioProfilerGroupViewBackend); + } } } + if (m_AudioProfilerGroupView != null) + m_AudioProfilerGroupView.OnGUI(treeRect, m_ShowDetailedAudioPane == ProfilerAudioView.Channels); } - if (m_AudioProfilerGroupView != null) - m_AudioProfilerGroupView.OnGUI(treeRect, m_ShowDetailedAudioPane == ProfilerAudioView.Channels); + if (property != null) + property.Dispose(); } - if (property != null) - property.Dispose(); + } + else + { + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + DrawOverviewText(m_CurrentArea); } } diff --git a/Modules/ProfilerEditor/Public/ProfilerAPI.bindings.cs b/Modules/ProfilerEditor/Public/ProfilerAPI.bindings.cs index b2b687f222..75bb8169db 100644 --- a/Modules/ProfilerEditor/Public/ProfilerAPI.bindings.cs +++ b/Modules/ProfilerEditor/Public/ProfilerAPI.bindings.cs @@ -54,7 +54,7 @@ public enum ProfilerMemoryView public enum ProfilerAudioView { - Stats = 0, + [Obsolete("This has been made obsolete. Audio stats are now shown on every subpane.", true)] Stats = 0, Channels = 1, Groups = 2, ChannelsAndGroups = 3, diff --git a/Modules/ProfilerEditor/Public/ProfilerFrameDataIterator.bindings.cs b/Modules/ProfilerEditor/Public/ProfilerFrameDataIterator.bindings.cs index 19037e8a21..c2ddb37866 100644 --- a/Modules/ProfilerEditor/Public/ProfilerFrameDataIterator.bindings.cs +++ b/Modules/ProfilerEditor/Public/ProfilerFrameDataIterator.bindings.cs @@ -63,6 +63,14 @@ public extern int depth get; } + /// + /// The maximal depth of the stacked samples. This count includes the thread root as well as counters. + /// + public extern int maxDepth + { + get; + } + public extern string path { [NativeMethod("GetFunctionPath")] diff --git a/Modules/ShortcutManagerEditor/ConflictResolver.cs b/Modules/ShortcutManagerEditor/ConflictResolver.cs index e54fc6df84..0a6cde0f5a 100644 --- a/Modules/ShortcutManagerEditor/ConflictResolver.cs +++ b/Modules/ShortcutManagerEditor/ConflictResolver.cs @@ -12,7 +12,7 @@ namespace UnityEditor.ShortcutManagement { class ConflictResolver : IConflictResolver { - public void ResolveConflict(List keyCombinationSequence, List entries) + public void ResolveConflict(IEnumerable keyCombinationSequence, IEnumerable entries) { var builder = new StringBuilder(); diff --git a/Modules/ShortcutManagerEditor/ContextManager.cs b/Modules/ShortcutManagerEditor/ContextManager.cs index 9a0de4c520..c6709cd1f7 100644 --- a/Modules/ShortcutManagerEditor/ContextManager.cs +++ b/Modules/ShortcutManagerEditor/ContextManager.cs @@ -16,6 +16,7 @@ interface IContextManager bool HasAnyPriorityContext(); bool HasPriorityContextOfType(Type type); bool HasActiveContextOfType(Type type); + bool playModeContextIsActive { get; } object GetContextInstanceOfType(Type type); } @@ -32,7 +33,19 @@ internal class GlobalContext {} List m_ToolContexts = new List(); - public int activeContextCount => 1 + ((m_FocusedWindow != null && m_FocusedWindow.IsAlive && m_FocusedWindow.Target != null) ? 1 : 0) + m_PriorityContexts.Count(c => c.active) + m_ToolContexts.Count(c => c.active); + public int activeContextCount => 1 + ((focusedWindow != null) ? 1 : 0) + m_PriorityContexts.Count(c => c.active) + m_ToolContexts.Count(c => c.active); + + public bool playModeContextIsActive => focusedWindow is GameView && EditorApplication.isPlaying; + + private EditorWindow focusedWindow + { + get + { + if (m_FocusedWindow != null && m_FocusedWindow.IsAlive && m_FocusedWindow.Target != null) + return m_FocusedWindow.Target as EditorWindow; + return null; + } + } public void SetFocusedWindow(EditorWindow window) { diff --git a/Modules/ShortcutManagerEditor/Directory.cs b/Modules/ShortcutManagerEditor/Directory.cs index e718465fd2..05f04b2292 100644 --- a/Modules/ShortcutManagerEditor/Directory.cs +++ b/Modules/ShortcutManagerEditor/Directory.cs @@ -56,6 +56,12 @@ public void FindShortcutEntries(List combinationSequence, IConte { if (!contextManager.HasActiveContextOfType(shortcutEntry.context)) continue; + if (shortcutEntry.type != ShortcutType.Menu && contextManager.playModeContextIsActive) + // Emulate old play mode shortcut behavior + // * Menu shortcuts are always active + // * Non-menu shortcuts only apply when the game view does not have focus + continue; + outputShortcuts.Add(shortcutEntry); } } diff --git a/Modules/ShortcutManagerEditor/IConflictResolver.cs b/Modules/ShortcutManagerEditor/IConflictResolver.cs index 06f79d4fdf..845e86f5e0 100644 --- a/Modules/ShortcutManagerEditor/IConflictResolver.cs +++ b/Modules/ShortcutManagerEditor/IConflictResolver.cs @@ -8,5 +8,5 @@ interface IConflictResolver { - void ResolveConflict(List keyCombinationSequence, List entries); + void ResolveConflict(IEnumerable keyCombinationSequence, IEnumerable entries); } diff --git a/Modules/ShortcutManagerEditor/KeyCombination.cs b/Modules/ShortcutManagerEditor/KeyCombination.cs new file mode 100644 index 0000000000..a741cb5b22 --- /dev/null +++ b/Modules/ShortcutManagerEditor/KeyCombination.cs @@ -0,0 +1,232 @@ +// 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.Text; +using System.Collections.Generic; + +using UnityEngine; + +namespace UnityEditor.ShortcutManagement +{ + [Serializable] + struct KeyCombination + { + [SerializeField] + KeyCode m_KeyCode; + [SerializeField] + ShortcutModifiers m_Modifiers; + + public KeyCode keyCode => m_KeyCode; + public ShortcutModifiers modifiers => m_Modifiers; + + public KeyCombination(KeyCode keyCode, ShortcutModifiers shortcutModifiers = ShortcutModifiers.None) + { + m_KeyCode = keyCode; + m_Modifiers = shortcutModifiers; + } + + internal static KeyCombination FromKeyboardInput(Event evt) + { + return new KeyCombination(evt.keyCode, ConvertEventModifiersToShortcutModifiers(evt.modifiers, false)); + } + + internal static KeyCombination ParseLegacyBindingString(string binding) + { + var keyEvent = Event.KeyboardEvent(binding); + return new KeyCombination(keyEvent.keyCode, ConvertEventModifiersToShortcutModifiers(keyEvent.modifiers, true)); + } + + private static ShortcutModifiers ConvertEventModifiersToShortcutModifiers(EventModifiers eventModifiers, bool coalesceCommandAndControl) + { + ShortcutModifiers modifiers = ShortcutModifiers.None; + if ((eventModifiers & EventModifiers.Alt) != 0) + modifiers |= ShortcutModifiers.Alt; + if ((eventModifiers & EventModifiers.Shift) != 0) + modifiers |= ShortcutModifiers.Shift; + + if (coalesceCommandAndControl) + { + if ((eventModifiers & (EventModifiers.Command | EventModifiers.Control)) != 0) + modifiers |= ShortcutModifiers.ControlOrCommand; + } + else if (Application.platform == RuntimePlatform.OSXEditor && (eventModifiers & EventModifiers.Command) != 0) + modifiers |= ShortcutModifiers.ControlOrCommand; + else if (Application.platform != RuntimePlatform.OSXEditor && (eventModifiers & EventModifiers.Control) != 0) + modifiers |= ShortcutModifiers.ControlOrCommand; + + return modifiers; + } + + public bool alt => (modifiers & ShortcutModifiers.Alt) == ShortcutModifiers.Alt; + public bool controlOrCommand => (modifiers & ShortcutModifiers.ControlOrCommand) == ShortcutModifiers.ControlOrCommand; + public bool shift => (modifiers & ShortcutModifiers.Shift) == ShortcutModifiers.Shift; + + public Event ToKeyboardEvent() + { + Event e = new Event(); + e.type = EventType.KeyDown; + e.alt = alt; + e.command = controlOrCommand && Application.platform == RuntimePlatform.OSXEditor; + e.control = controlOrCommand && Application.platform != RuntimePlatform.OSXEditor; + e.shift = shift; + e.keyCode = keyCode; + return e; + } + + public static string SequenceToString(IEnumerable keyCombinations) + { + if (!keyCombinations.Any()) + return ""; + + var builder = new StringBuilder(); + + builder.Append(keyCombinations.First()); + + foreach (var keyCombination in keyCombinations.Skip(1)) + { + builder.Append(", "); + builder.Append(keyCombination); + } + + return builder.ToString(); + } + + public override string ToString() + { + var builder = new StringBuilder(); + VisualizeModifiers(modifiers, builder); + VisualizeKeyCode(keyCode, builder); + return builder.ToString(); + } + + public string ToMenuShortcutString() + { + if (keyCode == KeyCode.None) + return string.Empty; + + var builder = new StringBuilder(); + if ((modifiers & ShortcutModifiers.Alt) != 0) + builder.Append("&"); + if ((modifiers & ShortcutModifiers.Shift) != 0) + builder.Append("#"); + if ((modifiers & ShortcutModifiers.ControlOrCommand) != 0) + builder.Append("%"); + if (modifiers == ShortcutModifiers.None) + builder.Append("_"); + + VisualizeKeyCode(keyCode, builder); + + return builder.ToString(); + } + + static void VisualizeModifiers(ShortcutModifiers modifiers, StringBuilder builder) + { + if (Application.platform == RuntimePlatform.OSXEditor) + { + if ((modifiers & ShortcutModifiers.Alt) != 0) + builder.Append("⌥"); + if ((modifiers & ShortcutModifiers.Shift) != 0) + builder.Append("⇧"); + if ((modifiers & ShortcutModifiers.ControlOrCommand) != 0) + builder.Append("⌘"); + } + else + { + if ((modifiers & ShortcutModifiers.ControlOrCommand) != 0) + builder.Append("Ctrl+"); + if ((modifiers & ShortcutModifiers.Alt) != 0) + builder.Append("Alt+"); + if ((modifiers & ShortcutModifiers.Shift) != 0) + builder.Append("Shift+"); + } + } + + static void VisualizeKeyCode(KeyCode keyCode, StringBuilder builder) + { + if (!TryFormatKeycode(keyCode, builder)) + builder.Append(keyCode.ToString()); + } + + static bool TryFormatKeycode(KeyCode code, StringBuilder builder) + { + if (Application.platform == RuntimePlatform.OSXEditor) + { + switch (code) + { + case KeyCode.Return: + builder.Append("↩"); + break; + case KeyCode.Backspace: + builder.Append("⌫"); + break; + case KeyCode.Delete: + builder.Append("⌦"); + break; + case KeyCode.Escape: + builder.Append("⎋"); + break; + case KeyCode.RightArrow: + builder.Append("→"); + break; + case KeyCode.LeftArrow: + builder.Append("←"); + break; + case KeyCode.UpArrow: + builder.Append("↑"); + break; + case KeyCode.DownArrow: + builder.Append("↓"); + break; + case KeyCode.PageUp: + builder.Append("⇞"); + break; + case KeyCode.PageDown: + builder.Append("⇟"); + break; + case KeyCode.Home: + builder.Append("↖"); + break; + case KeyCode.End: + builder.Append("↘"); + break; + case KeyCode.Tab: + builder.Append("⇥"); + break; + default: + return false; + } + } + else + { + switch (code) + { + case KeyCode.Delete: + builder.Append("DEL"); + break; + case KeyCode.Backspace: + builder.Append("BACKSPACE"); + break; + case KeyCode.LeftArrow: + builder.Append("LEFT"); + break; + case KeyCode.RightArrow: + builder.Append("RIGHT"); + break; + case KeyCode.UpArrow: + builder.Append("UP"); + break; + case KeyCode.DownArrow: + builder.Append("DOWN"); + break; + default: + return false; + } + } + + return true; + } + } +} diff --git a/Modules/ShortcutManagerEditor/ShortcutAttribute.cs b/Modules/ShortcutManagerEditor/ShortcutAttribute.cs index b4c6a72fb2..7f741de2e3 100644 --- a/Modules/ShortcutManagerEditor/ShortcutAttribute.cs +++ b/Modules/ShortcutManagerEditor/ShortcutAttribute.cs @@ -41,11 +41,14 @@ public ShortcutAttribute(string identifier, Type context = null, string defaultK public override ShortcutEntry CreateShortcutEntry(MethodInfo methodInfo) { - var keyEvent = Event.KeyboardEvent(defaultKeyCombination); - var defaultCombination = new List(); - var keyCombination = new KeyCombination(keyEvent); - defaultCombination.Add(keyCombination); var identifier = new Identifier(methodInfo, this); + + IEnumerable defaultCombination; + if (defaultKeyCombination == null) + defaultCombination = Enumerable.Empty(); + else + defaultCombination = new[] { KeyCombination.ParseLegacyBindingString(defaultKeyCombination) }; + var type = this is ClutchShortcutAttribute ? ShortcutType.Clutch : ShortcutType.Action; var methodParams = methodInfo.GetParameters(); Action action; diff --git a/Modules/ShortcutManagerEditor/ShortcutAttributeDiscoveryProvider.cs b/Modules/ShortcutManagerEditor/ShortcutAttributeDiscoveryProvider.cs index 196892e6dd..bd8c65d1ab 100644 --- a/Modules/ShortcutManagerEditor/ShortcutAttributeDiscoveryProvider.cs +++ b/Modules/ShortcutManagerEditor/ShortcutAttributeDiscoveryProvider.cs @@ -2,6 +2,7 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -39,15 +40,15 @@ public IEnumerable GetDefinedShortcuts() { var entries = new List(); var names = new List(); - var shortcuts = new List(); - Menu.GetMenuItemShortcuts(names, shortcuts); + var defaultShortcuts = new List(); + Menu.GetMenuItemDefaultShortcuts(names, defaultShortcuts); entries.Capacity += names.Count; for (int index = 0; index < names.Count; ++index) { var keys = new List(); - if (!string.IsNullOrEmpty(shortcuts[index])) - keys.Add(new KeyCombination(Event.KeyboardEvent(shortcuts[index]))); + if (!string.IsNullOrEmpty(defaultShortcuts[index])) + keys.Add(KeyCombination.ParseLegacyBindingString(defaultShortcuts[index])); entries.Add(new MenuItemEntryDiscoveryInfo(names[index], keys)); } @@ -139,7 +140,8 @@ public MenuItemEntryDiscoveryInfo(string menuItemPath, List keys m_KeyCombinations = keys; m_MenuItemPath = menuItemPath; - m_ShortcutEntry = new ShortcutEntry(new Identifier(m_MenuItemPath), m_KeyCombinations, null, null, ShortcutType.Menu); + Action menuAction = (args) => { EditorApplication.ExecuteMenuItem(m_MenuItemPath); }; + m_ShortcutEntry = new ShortcutEntry(new Identifier(m_MenuItemPath), m_KeyCombinations, menuAction, null, ShortcutType.Menu); } public ShortcutEntry GetShortcutEntry() diff --git a/Modules/ShortcutManagerEditor/ShortcutController.cs b/Modules/ShortcutManagerEditor/ShortcutController.cs index 068fe5f33c..34f3303ddc 100644 --- a/Modules/ShortcutManagerEditor/ShortcutController.cs +++ b/Modules/ShortcutManagerEditor/ShortcutController.cs @@ -16,9 +16,17 @@ static ShortcutIntegration() { InitializeController(); EditorApplication.globalEventHandler += EventHandler; + EditorApplication.doPressedKeysTriggerAnyShortcut += HasAnyEntriesHandler; // Need to reinitialize after project load if we want menu items EditorApplication.projectWasLoaded += InitializeController; + + EditorApplication.focusChanged += OnFocusChanged; + } + + static bool HasAnyEntriesHandler() + { + return instance.HasAnyEntries(Event.current); } static void EventHandler() @@ -27,6 +35,17 @@ static void EventHandler() instance.HandleKeyEvent(Event.current); } + static void OnInvokingAction(ShortcutEntry shortcutEntry, ShortcutArguments shortcutArguments) + { + // Separate shortcut actions into different undo groups + Undo.IncrementCurrentGroup(); + } + + static void OnFocusChanged(bool isFocused) + { + instance.trigger.ResetActiveClutches(); + } + static void InitializeController() { var shortcutProviders = new IDiscoveryShortcutProvider[] @@ -39,6 +58,7 @@ static void InitializeController() var discovery = new Discovery(shortcutProviders, identifierConflictHandler, invalidContextReporter); instance = new ShortcutController(discovery); instance.Initialize(instance.profileManager); + instance.trigger.invokingAction += OnInvokingAction; } } @@ -49,6 +69,7 @@ class ShortcutController public IShortcutProfileManager profileManager { get; } public IDirectory directory => m_Directory; + public Trigger trigger => m_Trigger; ContextManager m_ContextManager = new ContextManager(); @@ -67,6 +88,11 @@ internal void Initialize(IShortcutProfileManager sender) m_Trigger = new Trigger(directory, new ConflictResolver()); } + internal bool HasAnyEntries(Event evt) + { + return m_Trigger.HasAnyEntries(); + } + internal void HandleKeyEvent(Event evt) { m_Trigger.HandleKeyEvent(evt, contextManager); diff --git a/Modules/ShortcutManagerEditor/ShortcutEntry.cs b/Modules/ShortcutManagerEditor/ShortcutEntry.cs index 298751209b..c68b8a5b8a 100644 --- a/Modules/ShortcutManagerEditor/ShortcutEntry.cs +++ b/Modules/ShortcutManagerEditor/ShortcutEntry.cs @@ -3,11 +3,9 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; -using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text; -using UnityEngine; +using System.Collections.Generic; namespace UnityEditor.ShortcutManagement { @@ -74,10 +72,10 @@ class ShortcutEntry public Type context => m_Context; public ShortcutType type => m_Type; - internal ShortcutEntry(Identifier id, List defaultCombination, Action action, Type context, ShortcutType type) + internal ShortcutEntry(Identifier id, IEnumerable defaultCombination, Action action, Type context, ShortcutType type) { m_Identifier = id; - m_DefaultCombinations = new List(defaultCombination); + m_DefaultCombinations = defaultCombination.ToList(); m_Context = context ?? ContextManager.globalContextType; m_Action = action; m_Type = type; @@ -185,204 +183,4 @@ enum ShortcutModifiers ControlOrCommand = 2, Shift = 4 } - - [Serializable] - struct KeyCombination - { - [SerializeField] - KeyCode m_KeyCode; - [SerializeField] - ShortcutModifiers m_Modifiers; - - public KeyCode keyCode => m_KeyCode; - public ShortcutModifiers modifiers => m_Modifiers; - - public KeyCombination(KeyCode keyCode, ShortcutModifiers shortcutModifiers = ShortcutModifiers.None) - { - m_KeyCode = keyCode; - m_Modifiers = shortcutModifiers; - } - - internal KeyCombination(Event evt) - { - m_KeyCode = evt.keyCode; - m_Modifiers = ShortcutModifiers.None; - - if (evt.alt) - m_Modifiers |= ShortcutModifiers.Alt; - if (evt.control || evt.command) - m_Modifiers |= ShortcutModifiers.ControlOrCommand; - if (evt.shift) - m_Modifiers |= ShortcutModifiers.Shift; - } - - public bool alt => (modifiers & ShortcutModifiers.Alt) == ShortcutModifiers.Alt; - public bool controlOrCommand => (modifiers & ShortcutModifiers.ControlOrCommand) == ShortcutModifiers.ControlOrCommand; - public bool shift => (modifiers & ShortcutModifiers.Shift) == ShortcutModifiers.Shift; - - public Event ToKeyboardEvent() - { - Event e = new Event(); - e.type = EventType.KeyDown; - e.alt = alt; - e.command = controlOrCommand && Application.platform == RuntimePlatform.OSXEditor; - e.control = controlOrCommand && Application.platform != RuntimePlatform.OSXEditor; - e.shift = shift; - e.keyCode = keyCode; - return e; - } - - public static string SequenceToString(IEnumerable keyCombinations) - { - if (!keyCombinations.Any()) - return ""; - - var builder = new StringBuilder(); - - builder.Append(keyCombinations.First()); - - foreach (var keyCombination in keyCombinations.Skip(1)) - { - builder.Append(", "); - builder.Append(keyCombination); - } - - return builder.ToString(); - } - - public override string ToString() - { - var builder = new StringBuilder(); - VisualizeModifiers(modifiers, builder); - VisualizeKeyCode(keyCode, builder); - return builder.ToString(); - } - - public string ToMenuShortcutString() - { - if (keyCode == KeyCode.None) - return string.Empty; - - var builder = new StringBuilder(); - if ((modifiers & ShortcutModifiers.Alt) != 0) - builder.Append("&"); - if ((modifiers & ShortcutModifiers.Shift) != 0) - builder.Append("#"); - if ((modifiers & ShortcutModifiers.ControlOrCommand) != 0) - builder.Append("%"); - if (modifiers == ShortcutModifiers.None) - builder.Append("_"); - - VisualizeKeyCode(keyCode, builder); - - return builder.ToString(); - } - - static void VisualizeModifiers(ShortcutModifiers modifiers, StringBuilder builder) - { - if (Application.platform == RuntimePlatform.OSXEditor) - { - if ((modifiers & ShortcutModifiers.Alt) != 0) - builder.Append("⌥"); - if ((modifiers & ShortcutModifiers.Shift) != 0) - builder.Append("⇧"); - if ((modifiers & ShortcutModifiers.ControlOrCommand) != 0) - builder.Append("⌘"); - } - else - { - if ((modifiers & ShortcutModifiers.ControlOrCommand) != 0) - builder.Append("Ctrl+"); - if ((modifiers & ShortcutModifiers.Alt) != 0) - builder.Append("Alt+"); - if ((modifiers & ShortcutModifiers.Shift) != 0) - builder.Append("Shift+"); - } - } - - static void VisualizeKeyCode(KeyCode keyCode, StringBuilder builder) - { - if (!TryFormatKeycode(keyCode, builder)) - builder.Append(keyCode.ToString()); - } - - static bool TryFormatKeycode(KeyCode code, StringBuilder builder) - { - if (Application.platform == RuntimePlatform.OSXEditor) - { - switch (code) - { - case KeyCode.Return: - builder.Append("↩"); - break; - case KeyCode.Backspace: - builder.Append("⌫"); - break; - case KeyCode.Delete: - builder.Append("⌦"); - break; - case KeyCode.Escape: - builder.Append("⎋"); - break; - case KeyCode.RightArrow: - builder.Append("→"); - break; - case KeyCode.LeftArrow: - builder.Append("←"); - break; - case KeyCode.UpArrow: - builder.Append("↑"); - break; - case KeyCode.DownArrow: - builder.Append("↓"); - break; - case KeyCode.PageUp: - builder.Append("⇞"); - break; - case KeyCode.PageDown: - builder.Append("⇟"); - break; - case KeyCode.Home: - builder.Append("↖"); - break; - case KeyCode.End: - builder.Append("↘"); - break; - case KeyCode.Tab: - builder.Append("⇥"); - break; - default: - return false; - } - } - else - { - switch (code) - { - case KeyCode.Delete: - builder.Append("DEL"); - break; - case KeyCode.Backspace: - builder.Append("BACKSPACE"); - break; - case KeyCode.LeftArrow: - builder.Append("LEFT"); - break; - case KeyCode.RightArrow: - builder.Append("RIGHT"); - break; - case KeyCode.UpArrow: - builder.Append("UP"); - break; - case KeyCode.DownArrow: - builder.Append("DOWN"); - break; - default: - return false; - } - } - - return true; - } - } } diff --git a/Modules/ShortcutManagerEditor/ShortcutProfileManager.cs b/Modules/ShortcutManagerEditor/ShortcutProfileManager.cs index 915b3180f3..c0262cec19 100644 --- a/Modules/ShortcutManagerEditor/ShortcutProfileManager.cs +++ b/Modules/ShortcutManagerEditor/ShortcutProfileManager.cs @@ -91,15 +91,14 @@ IEnumerable methodsWithFormerlyPrefKeyAs, List entrie var prefKeyAttr = (FormerlyPrefKeyAsAttribute)Attribute.GetCustomAttribute(method, typeof(FormerlyPrefKeyAsAttribute)); - var prefKeyDefaultValue = new KeyCombination(Event.KeyboardEvent(prefKeyAttr.defaultValue)); + var prefKeyDefaultValue = KeyCombination.ParseLegacyBindingString(prefKeyAttr.defaultValue); string name; + string binding; Event keyboardEvent; - var parsed = PrefKey.TryParseUniquePrefString( - EditorPrefs.GetString(prefKeyAttr.name, prefKeyAttr.defaultValue), out name, out keyboardEvent - ); + var parsed = PrefKey.TryParseUniquePrefString(EditorPrefs.GetString(prefKeyAttr.name, prefKeyAttr.defaultValue), out name, out keyboardEvent, out binding); if (!parsed) continue; - var prefKeyCurrentValue = new KeyCombination(keyboardEvent); + var prefKeyCurrentValue = KeyCombination.ParseLegacyBindingString(binding); // only migrate pref keys that the user actually overwrote if (!prefKeyCurrentValue.Equals(prefKeyDefaultValue)) entry.SetOverride(new List { prefKeyCurrentValue }); diff --git a/Modules/ShortcutManagerEditor/Trigger.cs b/Modules/ShortcutManagerEditor/Trigger.cs index 3aaf584b68..df70a6d649 100644 --- a/Modules/ShortcutManagerEditor/Trigger.cs +++ b/Modules/ShortcutManagerEditor/Trigger.cs @@ -17,7 +17,8 @@ class Trigger List m_KeyCombinationSequence = new List(); List m_Entries = new List(); Dictionary> m_ActiveClutches = new Dictionary>(); - HashSet m_KeysDown = new HashSet(); + + public event Action invokingAction; public Trigger(IDirectory directory, IConflictResolver conflictResolver) { @@ -32,7 +33,6 @@ public void HandleKeyEvent(Event evt, IContextManager contextManager) if (evt.type == EventType.KeyUp) { - m_KeysDown.Remove(evt.keyCode); Tuple clutchTuple; if (m_ActiveClutches.TryGetValue(evt.keyCode, out clutchTuple)) { @@ -44,19 +44,20 @@ public void HandleKeyEvent(Event evt, IContextManager contextManager) context = clutchContext, state = ShortcutState.End }; + invokingAction?.Invoke(clutchTuple.Item1, args); clutchTuple.Item1.action(args); } return; } - if (m_KeysDown.Contains(evt.keyCode)) + // Use the event and return if the key is currently used in an active clutch + if (m_ActiveClutches.ContainsKey(evt.keyCode)) { evt.Use(); return; } - m_KeysDown.Add(evt.keyCode); - var keyCodeCombination = new KeyCombination(evt); + var keyCodeCombination = KeyCombination.FromKeyboardInput(evt); m_KeyCombinationSequence.Add(keyCodeCombination); // Ignore event if sequence is empty @@ -64,26 +65,24 @@ public void HandleKeyEvent(Event evt, IContextManager contextManager) return; m_Directory.FindShortcutEntries(m_KeyCombinationSequence, contextManager, m_Entries); + IEnumerable entries = m_Entries; // Deal ONLY with prioritycontext - if (m_Entries.Count > 1 && contextManager.HasAnyPriorityContext()) + if (entries.Count() > 1 && contextManager.HasAnyPriorityContext()) { - var entry = m_Entries.FindAll(a => contextManager.HasPriorityContextOfType(a.context)); - if (entry.Any()) - { - m_Entries.Clear(); - m_Entries.AddRange(entry); - } + entries = m_Entries.FindAll(a => contextManager.HasPriorityContextOfType(a.context)); + if (!entries.Any()) + entries = m_Entries; } - switch (m_Entries.Count) + switch (entries.Count()) { case 0: Reset(); break; case 1: - var shortcutEntry = m_Entries.Single(); + var shortcutEntry = entries.Single(); if (ShortcutFullyMatchesKeyCombination(shortcutEntry)) { if (evt.keyCode != m_KeyCombinationSequence.Last().keyCode) @@ -95,6 +94,7 @@ public void HandleKeyEvent(Event evt, IContextManager contextManager) { case ShortcutType.Action: args.state = ShortcutState.End; + invokingAction?.Invoke(shortcutEntry, args); shortcutEntry.action(args); evt.Use(); Reset(); @@ -105,6 +105,7 @@ public void HandleKeyEvent(Event evt, IContextManager contextManager) { m_ActiveClutches.Add(evt.keyCode, new Tuple(shortcutEntry, args.context)); args.state = ShortcutState.Begin; + invokingAction?.Invoke(shortcutEntry, args); shortcutEntry.action(args); evt.Use(); Reset(); @@ -112,7 +113,8 @@ public void HandleKeyEvent(Event evt, IContextManager contextManager) break; case ShortcutType.Menu: args.state = ShortcutState.End; - EditorApplication.ExecuteMenuItem(shortcutEntry.identifier.path); + invokingAction?.Invoke(shortcutEntry, args); + shortcutEntry.action(args); evt.Use(); Reset(); break; @@ -121,20 +123,42 @@ public void HandleKeyEvent(Event evt, IContextManager contextManager) break; default: - if (HasConflicts(m_Entries, m_KeyCombinationSequence)) + if (HasConflicts(entries, m_KeyCombinationSequence)) { - m_ConflictResolver.ResolveConflict(m_KeyCombinationSequence, m_Entries); + m_ConflictResolver.ResolveConflict(m_KeyCombinationSequence, entries); + evt.Use(); Reset(); } break; } } + public void ResetActiveClutches() + { + foreach (var clutchTuple in m_ActiveClutches.Values) + { + var args = new ShortcutArguments + { + context = clutchTuple.Item2, + state = ShortcutState.End, + }; + invokingAction?.Invoke(clutchTuple.Item1, args); + clutchTuple.Item1.action(args); + } + + m_ActiveClutches.Clear(); + } + + public bool HasAnyEntries() + { + return m_Entries.Any(); + } + // filtered entries are expected to all be in the same context and/or null context and they all are known to share the prefix - bool HasConflicts(List filteredEntries, List prefix) + bool HasConflicts(IEnumerable filteredEntries, List prefix) { if (filteredEntries.Any(e => e.FullyMatches(prefix))) - return filteredEntries.Count > 1; + return filteredEntries.Count() > 1; return false; } diff --git a/Modules/SpriteMaskEditor/Editor/Managed/SpriteMaskEditor.cs b/Modules/SpriteMaskEditor/Editor/Managed/SpriteMaskEditor.cs index 4822728472..a696ab596e 100644 --- a/Modules/SpriteMaskEditor/Editor/Managed/SpriteMaskEditor.cs +++ b/Modules/SpriteMaskEditor/Editor/Managed/SpriteMaskEditor.cs @@ -36,7 +36,8 @@ private static class Contents [MenuItem("GameObject/2D Object/Sprite Mask")] static void CreateSpriteMaskGameObject() { - var go = ObjectFactory.CreateGameObject("", typeof(SpriteMask)); + var go = ObjectFactory.CreateGameObject(""); + go.AddComponent(); if (Selection.activeObject is Sprite) go.GetComponent().sprite = (Sprite)Selection.activeObject; else if (Selection.activeObject is Texture2D) diff --git a/Modules/Terrain/Public/BrushTransform.cs b/Modules/Terrain/Public/BrushTransform.cs new file mode 100644 index 0000000000..229d3b07ea --- /dev/null +++ b/Modules/Terrain/Public/BrushTransform.cs @@ -0,0 +1,80 @@ +// 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.Collections.Generic; +using UnityEngine.Rendering; + +namespace UnityEngine.Experimental.TerrainAPI +{ + // represents a linear 2D transform between brush UV space and some other target XY space + // xy = u * brushU + v * brushV + brushOrigin + // uv = x * targetX + y * targetY + targetOrigin + public struct BrushTransform + { + public Vector2 brushOrigin { get; } // brush UV origin, in XY space + public Vector2 brushU { get; } // brush U vector, in XY space + public Vector2 brushV { get; } // brush V vector, in XY space + + public Vector2 targetOrigin { get; } // XY origin, in brush UV space + public Vector2 targetX { get; } // X vector, in brush UV space + public Vector2 targetY { get; } // Y vector, in brush UV space + + public BrushTransform(Vector2 brushOrigin, Vector2 brushU, Vector2 brushV) + { + // invert the rotation matrix [BrushU, BrushV] + // this gives us [X, Y] vectors in brush UV space + // note we run the true inverse, to support non-orthogonal brush axes + float det = brushU.x * brushV.y - brushU.y * brushV.x; + float invDet = Mathf.Approximately(det, 0.0f) ? 1.0f : 1.0f / det; // for non-invert-able matrices, we do 'something' + Vector2 targetX = new Vector2(brushV.y, -brushU.y) * invDet; + Vector2 targetY = new Vector2(-brushV.x, brushU.x) * invDet; + + // calculate XY origin in brush UV space + Vector2 targetOrigin = -brushOrigin.x * targetX - brushOrigin.y * targetY; + + this.brushOrigin = brushOrigin; + this.brushU = brushU; + this.brushV = brushV; + this.targetOrigin = targetOrigin; + this.targetX = targetX; + this.targetY = targetY; + } + + public Rect GetBrushXYBounds() // get the XY bounding rectangle around the Brush [0,1] UV space + { + // compute all four corners of the brush [0,1] UV space + Vector2 pU = brushOrigin + brushU; + Vector2 pV = brushOrigin + brushV; + Vector2 pUV = brushOrigin + brushU + brushV; + + // compute min and max XY coordinates + float minX = Mathf.Min(Mathf.Min(brushOrigin.x, pU.x), Mathf.Min(pV.x, pUV.x)); + float maxX = Mathf.Max(Mathf.Max(brushOrigin.x, pU.x), Mathf.Max(pV.x, pUV.x)); + float minY = Mathf.Min(Mathf.Min(brushOrigin.y, pU.y), Mathf.Min(pV.y, pUV.y)); + float maxY = Mathf.Max(Mathf.Max(brushOrigin.y, pU.y), Mathf.Max(pV.y, pUV.y)); + + // return the XY bounding rectangle + return Rect.MinMaxRect(minX, minY, maxX, maxY); + } + + public static BrushTransform FromRect(Rect brushRect) + { + Vector2 brushOrigin = brushRect.min; + Vector2 brushU = new Vector2(brushRect.width, 0.0f); + Vector2 brushV = new Vector2(0.0f, brushRect.height); + return new BrushTransform(brushOrigin, brushU, brushV); + } + + public Vector2 ToBrushUV(Vector2 targetXY) + { + return targetXY.x * targetX + targetXY.y * targetY + targetOrigin; + } + + public Vector2 FromBrushUV(Vector2 brushUV) + { + return brushUV.x * brushU + brushUV.y * brushV + brushOrigin; + } + } +} diff --git a/Modules/Terrain/Public/PaintContext.cs b/Modules/Terrain/Public/PaintContext.cs new file mode 100644 index 0000000000..1ada108767 --- /dev/null +++ b/Modules/Terrain/Public/PaintContext.cs @@ -0,0 +1,593 @@ +// 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.Collections.Generic; +using UnityEngine.Rendering; + +namespace UnityEngine.Experimental.TerrainAPI +{ + public class PaintContext + { + // initialized by constructor + public Terrain originTerrain { get; } // the terrain that defines the coordinate system and world space position of this PaintContext + public RectInt pixelRect { get; } // the rectangle, in target texture pixels on the originTerrain, that this paint context represents + public int targetTextureWidth { get; } // the size of the target texture, per terrain tile + public int targetTextureHeight { get; } // the size of the target texture, per terrain tile + public Vector2 pixelSize { get; } // size of a paint context pixel in object/terrain/world space + + // initialized by CreateRenderTargets() + public RenderTexture sourceRenderTexture { get { return m_SourceRenderTexture; } } // the original data + public RenderTexture destinationRenderTexture { get { return m_DestinationRenderTexture; } } // the modified data (you render to this) + public RenderTexture oldRenderTexture { get { return m_OldRenderTexture; } } // active render texture at the time CreateRenderTargets() is called, restored on Cleanup() + + public int terrainCount { get { return m_TerrainTiles.Count; } } + public Terrain GetTerrain(int terrainIndex) + { + return m_TerrainTiles[terrainIndex].terrain; + } + + public RectInt GetClippedPixelRectInTerrainPixels(int terrainIndex) + { + return m_TerrainTiles[terrainIndex].clippedLocalPixels; + } + + public RectInt GetClippedPixelRectInRenderTexturePixels(int terrainIndex) + { + return m_TerrainTiles[terrainIndex].clippedPCPixels; + } + + // initialized by constructor + private List m_TerrainTiles; // all terrain tiles touched by this paint context + + // initialized by CreateRenderTargets() + private RenderTexture m_SourceRenderTexture; + private RenderTexture m_DestinationRenderTexture; + private RenderTexture m_OldRenderTexture; + + internal class TerrainTile + { + public TerrainTile() {} + public TerrainTile(Terrain newTerrain, int tileOriginPixelsX, int tileOriginPixelsY) { terrain = newTerrain; tileOriginPixels = new Vector2Int(tileOriginPixelsX, tileOriginPixelsY); } + + public Terrain terrain; // the terrain object for this tile + public Vector2Int tileOriginPixels; // coordinates of this terrain tile in originTerrain target texture pixels + + public RectInt clippedLocalPixels; // the tile pixels touched by this PaintContext (in local target texture pixels) + public RectInt clippedPCPixels; // the tile pixels touched by this PaintContext (in PaintContext/source/destRenderTexture pixels) + + public int mapIndex; // for splatmap operations, the splatmap index on this Terrain containing the desired TerrainLayer weight + public int channelIndex; // for splatmap operations, the channel on the splatmap containing the desired TerrainLayer weight + } + + [Flags] + internal enum ToolAction + { + None = 0, + PaintHeightmap = 1 << 0, + PaintTexture = 1 << 1, + } + + // TerrainPaintUtilityEditor hooks to this event to do automatic undo + internal static event Action onTerrainTileBeforePaint; + + public PaintContext(Terrain terrain, RectInt pixelRect, int targetTextureWidth, int targetTextureHeight) + { + this.originTerrain = terrain; + this.pixelRect = pixelRect; + this.targetTextureWidth = targetTextureWidth; + this.targetTextureHeight = targetTextureHeight; + TerrainData terrainData = terrain.terrainData; + this.pixelSize = new Vector2( + terrainData.size.x / (targetTextureWidth - 1.0f), + terrainData.size.z / (targetTextureHeight - 1.0f)); + + FindTerrainTiles(); + ClipTerrainTiles(); + } + + public static PaintContext CreateFromBounds(Terrain terrain, Rect boundsInTerrainSpace, int inputTextureWidth, int inputTextureHeight, int extraBorderPixels = 0) + { + return new PaintContext( + terrain, + TerrainPaintUtility.CalcPixelRectFromBounds(terrain, boundsInTerrainSpace, inputTextureWidth, inputTextureHeight, extraBorderPixels), + inputTextureWidth, inputTextureHeight); + } + + internal void FindTerrainTiles() + { + m_TerrainTiles = new List(); + + Terrain left = originTerrain.leftNeighbor; + Terrain right = originTerrain.rightNeighbor; + Terrain top = originTerrain.topNeighbor; + Terrain bottom = originTerrain.bottomNeighbor; + + bool wantLeft = (pixelRect.x < 0); + bool wantRight = (pixelRect.xMax > (targetTextureWidth - 1)); + bool wantTop = (pixelRect.yMax > (targetTextureHeight - 1)); + bool wantBottom = (pixelRect.y < 0); + + if (wantLeft && wantRight) + { + Debug.LogWarning("PaintContext pixelRect is too large! It should touch a maximum of 2 Terrains horizontally."); + wantRight = false; + } + + if (wantTop && wantBottom) + { + Debug.LogWarning("PaintContext pixelRect is too large! It should touch a maximum of 2 Terrains vertically."); + wantBottom = false; + } + + // add center tile + TerrainTile tile = new TerrainTile(originTerrain, 0, 0); + m_TerrainTiles.Add(tile); + + // add horizontal and vertical neighbors + Terrain horiz = null; + Terrain vert = null; + Terrain cornerTerrain = null; + + int horizTileDelta = 0; // how many tiles to move horizontally + int vertTileDelta = 0; // how many tiles to move vertically + + if (wantLeft) + { + horizTileDelta = -1; + horiz = left; + } + else if (wantRight) + { + horizTileDelta = 1; + horiz = right; + } + + if (wantTop) + { + vertTileDelta = 1; + vert = top; + } + else if (wantBottom) + { + vertTileDelta = -1; + vert = bottom; + } + + if (horiz) + { + tile = new TerrainTile(horiz, horizTileDelta * (targetTextureWidth - 1), 0); + m_TerrainTiles.Add(tile); + + // add corner, if we have a link + if (wantTop && horiz.topNeighbor) + cornerTerrain = horiz.topNeighbor; + else if (wantBottom && horiz.bottomNeighbor) + cornerTerrain = horiz.bottomNeighbor; + } + + if (vert) + { + tile = new PaintContext.TerrainTile(vert, 0, vertTileDelta * (targetTextureHeight - 1)); + m_TerrainTiles.Add(tile); + + // add corner, if we have a link + if (wantLeft && vert.leftNeighbor) + cornerTerrain = vert.leftNeighbor; + else if (wantRight && vert.rightNeighbor) + cornerTerrain = vert.rightNeighbor; + } + + if (cornerTerrain != null) + { + tile = new TerrainTile(cornerTerrain, horizTileDelta * (targetTextureWidth - 1), vertTileDelta * (targetTextureHeight - 1)); + m_TerrainTiles.Add(tile); + } + } + + internal void ClipTerrainTiles() + { + for (int i = 0; i < m_TerrainTiles.Count; i++) + { + TerrainTile tile = m_TerrainTiles[i]; + tile.clippedLocalPixels = new RectInt(); + tile.clippedLocalPixels.x = Mathf.Max(0, pixelRect.x - tile.tileOriginPixels.x); + tile.clippedLocalPixels.y = Mathf.Max(0, pixelRect.y - tile.tileOriginPixels.y); + tile.clippedLocalPixels.xMax = Mathf.Min(targetTextureWidth, pixelRect.xMax - tile.tileOriginPixels.x); + tile.clippedLocalPixels.yMax = Mathf.Min(targetTextureHeight, pixelRect.yMax - tile.tileOriginPixels.y); + + tile.clippedPCPixels = new RectInt( + tile.clippedLocalPixels.x + tile.tileOriginPixels.x - pixelRect.x, + tile.clippedLocalPixels.y + tile.tileOriginPixels.y - pixelRect.y, + tile.clippedLocalPixels.width, + tile.clippedLocalPixels.height); + } + } + + public void CreateRenderTargets(RenderTextureFormat colorFormat) + { + m_SourceRenderTexture = RenderTexture.GetTemporary(pixelRect.width, pixelRect.height, 0, colorFormat, RenderTextureReadWrite.Linear); + m_DestinationRenderTexture = RenderTexture.GetTemporary(pixelRect.width, pixelRect.height, 0, colorFormat, RenderTextureReadWrite.Linear); + m_SourceRenderTexture.wrapMode = TextureWrapMode.Clamp; + m_SourceRenderTexture.filterMode = FilterMode.Point; + m_OldRenderTexture = RenderTexture.active; + } + + public void Cleanup(bool restoreRenderTexture = true) + { + if (restoreRenderTexture) + { + RenderTexture.active = m_OldRenderTexture; + } + RenderTexture.ReleaseTemporary(m_SourceRenderTexture); + RenderTexture.ReleaseTemporary(m_DestinationRenderTexture); + m_SourceRenderTexture = null; + m_DestinationRenderTexture = null; + m_OldRenderTexture = null; + } + + public void GatherHeightmap() + { + Material blitMaterial = TerrainPaintUtility.GetBlitMaterial(); + + RenderTexture.active = sourceRenderTexture; + GL.Clear(false, true, new Color(0.0f, 0.0f, 0.0f, 0.0f)); + + GL.PushMatrix(); + GL.LoadPixelMatrix(0, pixelRect.width, 0, pixelRect.height); + + for (int i = 0; i < m_TerrainTiles.Count; i++) + { + TerrainTile terrainTile = m_TerrainTiles[i]; + if (terrainTile.clippedLocalPixels.width == 0 || terrainTile.clippedLocalPixels.height == 0) + continue; + + Texture sourceTexture = terrainTile.terrain.terrainData.heightmapTexture; + if ((sourceTexture.width != targetTextureWidth) || (sourceTexture.height != targetTextureHeight)) + { + Debug.LogWarning("PaintContext heightmap operations must use the same resolution for all Terrains - mismatched Terrains are ignored.", terrainTile.terrain); + continue; + } + + FilterMode oldFilterMode = sourceTexture.filterMode; + + sourceTexture.filterMode = FilterMode.Point; + + blitMaterial.SetTexture("_MainTex", sourceTexture); + blitMaterial.SetPass(0); + + TerrainPaintUtility.DrawQuad(terrainTile.clippedPCPixels, terrainTile.clippedLocalPixels, sourceTexture); + + sourceTexture.filterMode = oldFilterMode; + } + + GL.PopMatrix(); + + RenderTexture.active = oldRenderTexture; + } + + public void ScatterHeightmap(string editorUndoName) + { + Material blitMaterial = TerrainPaintUtility.GetBlitMaterial(); + + for (int i = 0; i < m_TerrainTiles.Count; i++) + { + TerrainTile terrainTile = m_TerrainTiles[i]; + if (terrainTile.clippedLocalPixels.width == 0 || terrainTile.clippedLocalPixels.height == 0) + continue; + + RenderTexture heightmap = terrainTile.terrain.terrainData.heightmapTexture; + if ((heightmap.width != targetTextureWidth) || (heightmap.height != targetTextureHeight)) + { + Debug.LogWarning("PaintContext heightmap operations must use the same resolution for all Terrains - mismatched Terrains are ignored.", terrainTile.terrain); + continue; + } + + if (onTerrainTileBeforePaint != null) + onTerrainTileBeforePaint(terrainTile, ToolAction.PaintHeightmap, editorUndoName); + + RenderTexture.active = heightmap; + GL.PushMatrix(); + GL.LoadPixelMatrix(0, heightmap.width, 0, heightmap.height); + + destinationRenderTexture.filterMode = FilterMode.Point; + + blitMaterial.SetTexture("_MainTex", destinationRenderTexture); + blitMaterial.SetPass(0); + + TerrainPaintUtility.DrawQuad(terrainTile.clippedLocalPixels, terrainTile.clippedPCPixels, destinationRenderTexture); + + GL.PopMatrix(); + + terrainTile.terrain.terrainData.UpdateDirtyRegion(terrainTile.clippedLocalPixels.x, terrainTile.clippedLocalPixels.y, terrainTile.clippedLocalPixels.width, terrainTile.clippedLocalPixels.height, !terrainTile.terrain.drawInstanced); + OnTerrainPainted(terrainTile, ToolAction.PaintHeightmap); + } + } + + public void GatherNormals() + { + RenderTexture rt = originTerrain.normalmapTexture; + + Material blitMaterial = TerrainPaintUtility.GetBlitMaterial(); + + RenderTexture.active = sourceRenderTexture; + GL.Clear(false, true, new Color(0.5f, 0.5f, 0.5f, 0.5f)); + GL.PushMatrix(); + GL.LoadPixelMatrix(0, pixelRect.width, 0, pixelRect.height); + + for (int i = 0; i < m_TerrainTiles.Count; i++) + { + TerrainTile terrainTile = m_TerrainTiles[i]; + if (terrainTile.clippedLocalPixels.width == 0 || terrainTile.clippedLocalPixels.height == 0) + continue; + + Texture sourceTexture = terrainTile.terrain.normalmapTexture; + if ((sourceTexture.width != targetTextureWidth) || (sourceTexture.height != targetTextureHeight)) + { + Debug.LogWarning("PaintContext normalmap operations must use the same resolution for all Terrains - mismatched Terrains are ignored.", terrainTile.terrain); + continue; + } + + FilterMode oldFilterMode = sourceTexture.filterMode; + + sourceTexture.filterMode = FilterMode.Point; + + blitMaterial.SetTexture("_MainTex", sourceTexture); + blitMaterial.SetPass(0); + + TerrainPaintUtility.DrawQuad(terrainTile.clippedPCPixels, terrainTile.clippedLocalPixels, sourceTexture); + + sourceTexture.filterMode = oldFilterMode; + } + + GL.PopMatrix(); + + RenderTexture.active = oldRenderTexture; + } + + public void GatherAlphamap(TerrainLayer inputLayer, bool addLayerIfDoesntExist = true) + { + if (inputLayer == null) + return; + + int terrainLayerIndex = TerrainPaintUtility.FindTerrainLayerIndex(originTerrain, inputLayer); + if (terrainLayerIndex == -1 && addLayerIfDoesntExist) + terrainLayerIndex = TerrainPaintUtility.AddTerrainLayer(originTerrain, inputLayer); + + RenderTexture.active = sourceRenderTexture; + GL.Clear(false, true, new Color(0.0f, 0.0f, 0.0f, 0.0f)); + GL.PushMatrix(); + GL.LoadPixelMatrix(0, pixelRect.width, 0, pixelRect.height); + + Vector4[] layerMasks = { new Vector4(1, 0, 0, 0), new Vector4(0, 1, 0, 0), new Vector4(0, 0, 1, 0), new Vector4(0, 0, 0, 1) }; + + Material copyTerrainLayerMaterial = TerrainPaintUtility.GetCopyTerrainLayerMaterial(); + for (int i = 0; i < m_TerrainTiles.Count; i++) + { + TerrainTile terrainTile = m_TerrainTiles[i]; + if (terrainTile.clippedLocalPixels.width == 0 || terrainTile.clippedLocalPixels.height == 0) + continue; + + int tileLayerIndex = TerrainPaintUtility.FindTerrainLayerIndex(terrainTile.terrain, inputLayer); + if (tileLayerIndex == -1) + { + if (!addLayerIfDoesntExist) + { + // setting these to zero will prevent them from being used later + terrainTile.clippedLocalPixels.width = 0; + terrainTile.clippedLocalPixels.height = 0; + terrainTile.clippedPCPixels.width = 0; + terrainTile.clippedPCPixels.height = 0; + continue; + } + tileLayerIndex = TerrainPaintUtility.AddTerrainLayer(terrainTile.terrain, inputLayer); + } + + terrainTile.mapIndex = tileLayerIndex >> 2; + terrainTile.channelIndex = tileLayerIndex & 0x3; + + Texture sourceTexture = TerrainPaintUtility.GetTerrainAlphaMapChecked(terrainTile.terrain, terrainTile.mapIndex); + if ((sourceTexture.width != targetTextureWidth) || (sourceTexture.height != targetTextureHeight)) + { + Debug.LogWarning("PaintContext alphamap operations must use the same resolution for all Terrains - mismatched Terrains are ignored. (" + + sourceTexture.width + " x " + sourceTexture.height + ") != (" + targetTextureWidth + " x " + targetTextureHeight + ")", + terrainTile.terrain); + continue; + } + + FilterMode oldFilterMode = sourceTexture.filterMode; + sourceTexture.filterMode = FilterMode.Point; + + copyTerrainLayerMaterial.SetVector("_LayerMask", layerMasks[terrainTile.channelIndex]); + copyTerrainLayerMaterial.SetTexture("_MainTex", sourceTexture); + copyTerrainLayerMaterial.SetPass(0); + + TerrainPaintUtility.DrawQuad(terrainTile.clippedPCPixels, terrainTile.clippedLocalPixels, sourceTexture); + + sourceTexture.filterMode = oldFilterMode; + } + + GL.PopMatrix(); + + RenderTexture.active = oldRenderTexture; + } + + public void ScatterAlphamap(string editorUndoName) + { + Vector4[] layerMasks = { new Vector4(1, 0, 0, 0), new Vector4(0, 1, 0, 0), new Vector4(0, 0, 1, 0), new Vector4(0, 0, 0, 1) }; + + Material copyTerrainLayerMaterial = TerrainPaintUtility.GetCopyTerrainLayerMaterial(); + + for (int i = 0; i < m_TerrainTiles.Count; i++) + { + TerrainTile terrainTile = m_TerrainTiles[i]; + if (terrainTile.clippedLocalPixels.width == 0 || terrainTile.clippedLocalPixels.height == 0) + continue; + + if (onTerrainTileBeforePaint != null) + onTerrainTileBeforePaint(terrainTile, ToolAction.PaintTexture, editorUndoName); + + var rtdesc = new RenderTextureDescriptor(destinationRenderTexture.width, destinationRenderTexture.height, RenderTextureFormat.ARGB32); + rtdesc.sRGB = false; + rtdesc.useMipMap = false; + rtdesc.autoGenerateMips = false; + RenderTexture destTarget = RenderTexture.GetTemporary(rtdesc); + RenderTexture.active = destTarget; + + RectInt writeRect = terrainTile.clippedPCPixels; + + Rect readRect = new Rect( + writeRect.x / (float)pixelRect.width, + writeRect.y / (float)pixelRect.height, + writeRect.width / (float)pixelRect.width, + writeRect.height / (float)pixelRect.height); + + destinationRenderTexture.filterMode = FilterMode.Point; + + int mapIndex = terrainTile.mapIndex; + int channelIndex = terrainTile.channelIndex; + Texture2D sourceTexTargetChannel = terrainTile.terrain.terrainData.alphamapTextures[mapIndex]; + + for (int j = 0; j < terrainTile.terrain.terrainData.alphamapTextureCount; j++) + { + Texture2D sourceTex = terrainTile.terrain.terrainData.alphamapTextures[j]; + if ((sourceTex.width != targetTextureWidth) || (sourceTex.height != targetTextureHeight)) + { + Debug.LogWarning("PaintContext alphamap operations must use the same resolution for all Terrains - mismatched Terrains are ignored.", terrainTile.terrain); + continue; + } + + Rect combineRect = new Rect( + terrainTile.clippedLocalPixels.x / (float)sourceTex.width, + terrainTile.clippedLocalPixels.y / (float)sourceTex.height, + terrainTile.clippedLocalPixels.width / (float)sourceTex.width, + terrainTile.clippedLocalPixels.height / (float)sourceTex.height); + + copyTerrainLayerMaterial.SetTexture("_MainTex", destinationRenderTexture); + copyTerrainLayerMaterial.SetTexture("_OldAlphaMapTexture", sourceRenderTexture); + copyTerrainLayerMaterial.SetTexture("_OriginalTargetAlphaMap", sourceTexTargetChannel); + + copyTerrainLayerMaterial.SetTexture("_AlphaMapTexture", sourceTex); + copyTerrainLayerMaterial.SetVector("_LayerMask", j == mapIndex ? layerMasks[channelIndex] : Vector4.zero); + copyTerrainLayerMaterial.SetVector("_OriginalTargetAlphaMask", layerMasks[channelIndex]); + copyTerrainLayerMaterial.SetPass(1); + + GL.PushMatrix(); + GL.LoadPixelMatrix(0, destTarget.width, 0, destTarget.height); + + GL.Begin(GL.QUADS); + GL.Color(new Color(1.0f, 1.0f, 1.0f, 1.0f)); + + GL.MultiTexCoord2(0, readRect.x, readRect.y); + GL.MultiTexCoord2(1, combineRect.x, combineRect.y); + GL.Vertex3(writeRect.x, writeRect.y, 0.0f); + GL.MultiTexCoord2(0, readRect.x, readRect.yMax); + GL.MultiTexCoord2(1, combineRect.x, combineRect.yMax); + GL.Vertex3(writeRect.x, writeRect.yMax, 0.0f); + GL.MultiTexCoord2(0, readRect.xMax, readRect.yMax); + GL.MultiTexCoord2(1, combineRect.xMax, combineRect.yMax); + GL.Vertex3(writeRect.xMax, writeRect.yMax, 0.0f); + GL.MultiTexCoord2(0, readRect.xMax, readRect.y); + GL.MultiTexCoord2(1, combineRect.xMax, combineRect.y); + GL.Vertex3(writeRect.xMax, writeRect.y, 0.0f); + + GL.End(); + GL.PopMatrix(); + + if (TerrainPaintUtility.paintTextureUsesCopyTexture) + { + var rtdesc2 = new RenderTextureDescriptor(sourceTex.width, sourceTex.height, RenderTextureFormat.ARGB32); + rtdesc2.sRGB = false; + rtdesc2.useMipMap = true; + rtdesc2.autoGenerateMips = false; + var mips = RenderTexture.GetTemporary(rtdesc2); + if (!mips.IsCreated()) + mips.Create(); + + // Composes mip0 in a RT with full mipchain. + Graphics.CopyTexture(sourceTex, 0, 0, mips, 0, 0); + Graphics.CopyTexture(destTarget, 0, 0, writeRect.x, writeRect.y, writeRect.width, writeRect.height, mips, 0, 0, terrainTile.clippedLocalPixels.x, terrainTile.clippedLocalPixels.y); + mips.GenerateMips(); + + // Copy them into sourceTex. + Graphics.CopyTexture(mips, sourceTex); + + RenderTexture.ReleaseTemporary(mips); + } + else + { + GraphicsDeviceType deviceType = SystemInfo.graphicsDeviceType; + if (deviceType == GraphicsDeviceType.Metal || deviceType == GraphicsDeviceType.OpenGLCore) + sourceTex.ReadPixels(new Rect(writeRect.x, writeRect.y, writeRect.width, writeRect.height), terrainTile.clippedLocalPixels.x, terrainTile.clippedLocalPixels.y); + else + sourceTex.ReadPixels(new Rect(writeRect.x, destTarget.height - writeRect.y - writeRect.height, writeRect.width, writeRect.height), terrainTile.clippedLocalPixels.x, terrainTile.clippedLocalPixels.y); + sourceTex.Apply(); + } + } + + RenderTexture.active = null; + RenderTexture.ReleaseTemporary(destTarget); + + OnTerrainPainted(terrainTile, ToolAction.PaintTexture); + } + } + + // Collects modified terrain so that we can update some deferred operations at the mouse up event + private class PaintedTerrain + { + public Terrain terrain; + public ToolAction action; + }; + private static List s_PaintedTerrain = new List(); + + private static void OnTerrainPainted(PaintContext.TerrainTile tile, ToolAction action) + { + for (int i = 0; i < s_PaintedTerrain.Count; ++i) + { + if (tile.terrain == s_PaintedTerrain[i].terrain) + { + s_PaintedTerrain[i].action |= action; + return; + } + } + s_PaintedTerrain.Add(new PaintedTerrain { terrain = tile.terrain, action = action }); + } + + public static void ApplyDelayedActions() + { + for (int i = 0; i < s_PaintedTerrain.Count; ++i) + { + var pt = s_PaintedTerrain[i]; + if ((pt.action & ToolAction.PaintHeightmap) != 0) + { + pt.terrain.ApplyDelayedHeightmapModification(); + } + if ((pt.action & ToolAction.PaintTexture) != 0) + { + var terrainData = pt.terrain.terrainData; + if (terrainData == null) + continue; + terrainData.SetBaseMapDirty(); + if (TerrainPaintUtility.paintTextureUsesCopyTexture) + { + // pull the data from GPU to CPU + var rtdesc = new RenderTextureDescriptor(terrainData.alphamapResolution, terrainData.alphamapResolution, RenderTextureFormat.ARGB32); + rtdesc.sRGB = false; + rtdesc.useMipMap = false; + rtdesc.autoGenerateMips = false; + RenderTexture tmp = RenderTexture.GetTemporary(rtdesc); + for (int c = 0; c < terrainData.alphamapTextureCount; ++c) + { + Graphics.Blit(terrainData.alphamapTextures[c], tmp); + terrainData.alphamapTextures[c].ReadPixels(new Rect(0, 0, rtdesc.width, rtdesc.height), 0, 0, true); + } + RenderTexture.ReleaseTemporary(tmp); + } + } + } + + s_PaintedTerrain.Clear(); + } + } +} diff --git a/Modules/Terrain/Public/TerrainPaintUtility.cs b/Modules/Terrain/Public/TerrainPaintUtility.cs index 2089699697..fd5d6ba659 100644 --- a/Modules/Terrain/Public/TerrainPaintUtility.cs +++ b/Modules/Terrain/Public/TerrainPaintUtility.cs @@ -27,390 +27,96 @@ public static Material GetBuiltinPaintMaterial() return s_BuiltinPaintMaterial; } - public class TerrainTile + // returns a transform from terrain space to brush UV + public static BrushTransform CalculateBrushTransform( + Terrain terrain, Vector2 brushCenterTerrainUV, float brushSize, float brushRotationDegrees) { - public TerrainTile() { terrain = null; } - public TerrainTile(Terrain newTerrain, RectInt newRegion) { rect = newRegion; terrain = newTerrain; } - public Terrain terrain; - public RectInt rect; // pixel coordinates of this terrain tile in the paint context (a locally built space relative to the active 'center' tile) + float rotationRadians = brushRotationDegrees * Mathf.Deg2Rad; + float cos = Mathf.Cos(rotationRadians); + float sin = Mathf.Sin(rotationRadians); + Vector2 brushU = new Vector2(cos, -sin) * brushSize; + Vector2 brushV = new Vector2(sin, cos) * brushSize; - public int mapIndex; - public int channelIndex; + // calculate brush origin + Vector3 terrainSize = terrain.terrainData.size; + Vector2 brushCenterTerrainSpace = brushCenterTerrainUV * new Vector2(terrainSize.x, terrainSize.z); + Vector2 brushOrigin = brushCenterTerrainSpace - 0.5f * brushU - 0.5f * brushV; - // offsets used for gather / scatter - public Vector2Int readOffset; // offsets used when reading from the terrain heightmap - public Vector2Int writeOffset; // offsets used when copying from PaintContext clipped heightmap back to the terrain heightmap + BrushTransform xform = new BrushTransform(brushOrigin, brushU, brushV); + return xform; } - public class PaintContext + public static void BuildTransformPaintContextUVToPaintContextUV(PaintContext src, PaintContext dst, out Vector4 scaleOffset) { - public RectInt brushRect; // the rectangle represented by this paint context, in target texture pixels (for the active terrain tile) - public TerrainTile[] terrainTiles; // all terrain tiles touched by this paint context - public RectInt[] clippedTiles; // the intersection of brushRect with each of the terrain tiles above, clipped into local tile pixels - public Rect[] validPaintRects; // the area per tile where the source texture was able to read from - - public RenderTexture sourceRenderTexture; - public RenderTexture destinationRenderTexture; - - public RenderTexture oldRenderTexture; // active render texture before PaintContext was initialized - - - public void CalculateBrushRect(Terrain terrain, Rect bounds, int inputTextureWidth, int inputTextureHeight) - { - brushRect = CalcBrushRectInPixels(terrain, bounds, inputTextureWidth, inputTextureHeight); - } - - public void CreateTerrainTiles(Terrain terrain, int inputTextureWidth, int inputTextureHeight) - { - terrainTiles = FindTerrainTiles(terrain, inputTextureWidth, inputTextureHeight, brushRect); - clippedTiles = ClipTerrainTiles(terrainTiles, brushRect); - validPaintRects = new Rect[terrainTiles.Length]; - - for (int i = 0; i < terrainTiles.Length; ++i) - { - TerrainTile terrainTile = terrainTiles[i]; - validPaintRects[i] = new Rect( - clippedTiles[i].x + terrainTile.rect.x - brushRect.x, - clippedTiles[i].y + terrainTile.rect.y - brushRect.y, - clippedTiles[i].width, - clippedTiles[i].height); - } - } - - public void CreateRenderTargets(RenderTextureFormat colorFormat) - { - sourceRenderTexture = RenderTexture.GetTemporary(brushRect.width, brushRect.height, 0, colorFormat, RenderTextureReadWrite.Linear); - destinationRenderTexture = RenderTexture.GetTemporary(brushRect.width, brushRect.height, 0, colorFormat, RenderTextureReadWrite.Linear); - sourceRenderTexture.wrapMode = TextureWrapMode.Clamp; - sourceRenderTexture.filterMode = FilterMode.Point; - oldRenderTexture = RenderTexture.active; - } - - public void Cleanup() - { - RenderTexture.active = oldRenderTexture; - RenderTexture.ReleaseTemporary(sourceRenderTexture); - RenderTexture.ReleaseTemporary(destinationRenderTexture); - sourceRenderTexture = null; - destinationRenderTexture = null; - oldRenderTexture = null; - } - - public void GatherHeightmap(Terrain terrain) - { - RenderTexture rt = terrain.terrainData.heightmapTexture; - int heightmapWidth = rt.width; - int heightmapHeight = rt.height; - - Material blitMaterial = GetBlitMaterial(); - - RenderTexture.active = sourceRenderTexture; - - for (int i = 0; i < terrainTiles.Length; i++) - { - if (clippedTiles[i].width == 0 || clippedTiles[i].height == 0) - continue; - - TerrainTile terrainTile = terrainTiles[i]; - - Rect readRect = new Rect( - (clippedTiles[i].x + terrainTile.readOffset.x) / (float)heightmapWidth, - (clippedTiles[i].y + terrainTile.readOffset.y) / (float)heightmapHeight, - (clippedTiles[i].width) / (float)heightmapWidth, - (clippedTiles[i].height) / (float)heightmapHeight); - - Texture sourceTexture = terrainTile.terrain.terrainData.heightmapTexture; - FilterMode oldFilterMode = sourceTexture.filterMode; - - sourceTexture.filterMode = FilterMode.Point; - - blitMaterial.SetTexture("_MainTex", sourceTexture); - blitMaterial.SetPass(0); - - DrawQuad(brushRect.width, brushRect.height, readRect, validPaintRects[i]); - - sourceTexture.filterMode = oldFilterMode; - } - - RenderTexture.active = oldRenderTexture; - } - - public void ScatterHeightmap(string editorUndoName) - { - Material blitMaterial = GetBlitMaterial(); - - for (int i = 0; i < terrainTiles.Length; i++) - { - if (clippedTiles[i].width == 0 || clippedTiles[i].height == 0) - continue; - - TerrainTile terrainTile = terrainTiles[i]; - - if (onTerrainTileBeforePaint != null) - onTerrainTileBeforePaint(terrainTile, ToolAction.PaintHeightmap, editorUndoName); - - RenderTexture heightmap = terrainTile.terrain.terrainData.heightmapTexture; - RenderTexture.active = heightmap; - - Rect readRect = new Rect( - (clippedTiles[i].x + terrainTile.rect.x - brushRect.x + terrainTile.writeOffset.x) / (float)brushRect.width, - (clippedTiles[i].y + terrainTile.rect.y - brushRect.y + terrainTile.writeOffset.y) / (float)brushRect.height, - (clippedTiles[i].width) / (float)brushRect.width, - (clippedTiles[i].height) / (float)brushRect.height); - - Rect writeRect = new Rect( - clippedTiles[i].x, - clippedTiles[i].y, - clippedTiles[i].width, - clippedTiles[i].height); - - destinationRenderTexture.filterMode = FilterMode.Point; - - blitMaterial.SetTexture("_MainTex", destinationRenderTexture); - blitMaterial.SetPass(0); - - DrawQuad(heightmap.width, heightmap.height, readRect, writeRect); - - terrainTile.terrain.terrainData.UpdateDirtyRegion(clippedTiles[i].x, clippedTiles[i].y, clippedTiles[i].width, clippedTiles[i].height, !terrainTile.terrain.drawInstanced); - OnTerrainPainted(terrainTile, ToolAction.PaintHeightmap); - } - } - - public void GatherNormals(Terrain terrain) - { - RenderTexture rt = terrain.normalmapTexture; - - RenderTextureFormat colorFormat = rt.format; - int heightmapWidth = rt.width; - int heightmapHeight = rt.height; - - Material blitMaterial = GetBlitMaterial(); - - RenderTexture.active = sourceRenderTexture; - - for (int i = 0; i < terrainTiles.Length; i++) - { - if (clippedTiles[i].width == 0 || clippedTiles[i].height == 0) - continue; - - TerrainTile terrainTile = terrainTiles[i]; - - Rect readRect = new Rect( - (clippedTiles[i].x + terrainTile.readOffset.x) / (float)heightmapWidth, - (clippedTiles[i].y + terrainTile.readOffset.y) / (float)heightmapHeight, - (clippedTiles[i].width) / (float)heightmapWidth, - (clippedTiles[i].height) / (float)heightmapHeight); - - Texture sourceTexture = terrainTile.terrain.normalmapTexture; - FilterMode oldFilterMode = sourceTexture.filterMode; - - sourceTexture.filterMode = FilterMode.Point; - - blitMaterial.SetTexture("_MainTex", sourceTexture); - blitMaterial.SetPass(0); - - DrawQuad(brushRect.width, brushRect.height, readRect, validPaintRects[i]); - - sourceTexture.filterMode = oldFilterMode; - } - - RenderTexture.active = oldRenderTexture; - } - - public void GatherAlphamap(Terrain terrain, TerrainLayer inputLayer, bool addLayerIfDoesntExist = true) - { - if (inputLayer == null) - return; - - int terrainLayerIndex = FindTerrainLayerIndex(terrain, inputLayer); - if (terrainLayerIndex == -1 && addLayerIfDoesntExist) - terrainLayerIndex = AddTerrainLayer(terrain, inputLayer); - - Texture2D inputTexture = GetTerrainAlphaMapChecked(terrain, terrainLayerIndex >> 2); - - int inputTextureWidth = inputTexture.width; - int inputTextureHeight = inputTexture.height; - - RenderTexture.active = sourceRenderTexture; - - Vector4[] layerMasks = { new Vector4(1, 0, 0, 0), new Vector4(0, 1, 0, 0), new Vector4(0, 0, 1, 0), new Vector4(0, 0, 0, 1) }; - - Material copyTerrainLayerMaterial = GetCopyTerrainLayerMaterial(); - for (int i = 0; i < terrainTiles.Length; i++) - { - if (clippedTiles[i].width == 0 || clippedTiles[i].height == 0) - continue; - - TerrainTile terrainTile = terrainTiles[i]; - - Rect readRect = new Rect( - (clippedTiles[i].x + terrainTile.readOffset.x) / (float)inputTextureWidth, - (clippedTiles[i].y + terrainTile.readOffset.y) / (float)inputTextureHeight, - (clippedTiles[i].width) / (float)inputTextureWidth, - (clippedTiles[i].height) / (float)inputTextureHeight); - - int tileLayerIndex = FindTerrainLayerIndex(terrainTile.terrain, inputLayer); - if (tileLayerIndex == -1) - { - if (!addLayerIfDoesntExist) - { - // setting these to zero will prevent them from being used later - clippedTiles[i].width = 0; - clippedTiles[i].height = 0; - validPaintRects[i].width = 0; - validPaintRects[i].height = 0; - continue; - } - tileLayerIndex = AddTerrainLayer(terrainTile.terrain, inputLayer); - } - - terrainTile.mapIndex = tileLayerIndex >> 2; - terrainTile.channelIndex = tileLayerIndex & 0x3; - - Texture sourceTexture = GetTerrainAlphaMapChecked(terrainTile.terrain, terrainTile.mapIndex); - - FilterMode oldFilterMode = sourceTexture.filterMode; - sourceTexture.filterMode = FilterMode.Point; - - copyTerrainLayerMaterial.SetVector("_LayerMask", layerMasks[terrainTile.channelIndex]); - copyTerrainLayerMaterial.SetTexture("_MainTex", sourceTexture); - copyTerrainLayerMaterial.SetPass(0); - - DrawQuad(brushRect.width, brushRect.height, readRect, validPaintRects[i]); - - sourceTexture.filterMode = oldFilterMode; - } - - RenderTexture.active = oldRenderTexture; - } - - public void ScatterAlphamap(string editorUndoName) - { - Vector4[] layerMasks = { new Vector4(1, 0, 0, 0), new Vector4(0, 1, 0, 0), new Vector4(0, 0, 1, 0), new Vector4(0, 0, 0, 1) }; - - Material copyTerrainLayerMaterial = GetCopyTerrainLayerMaterial(); - - for (int i = 0; i < terrainTiles.Length; i++) - { - if (clippedTiles[i].width == 0 || clippedTiles[i].height == 0) - continue; - - TerrainTile terrainTile = terrainTiles[i]; - - if (onTerrainTileBeforePaint != null) - onTerrainTileBeforePaint(terrainTile, ToolAction.PaintTexture, editorUndoName); - - var rtdesc = new RenderTextureDescriptor(destinationRenderTexture.width, destinationRenderTexture.height, RenderTextureFormat.ARGB32); - rtdesc.sRGB = false; - rtdesc.useMipMap = false; - rtdesc.autoGenerateMips = false; - RenderTexture destTarget = RenderTexture.GetTemporary(rtdesc); - RenderTexture.active = destTarget; - - var writeRect = new RectInt( - clippedTiles[i].x + terrainTile.rect.x - brushRect.x + terrainTile.writeOffset.x, - clippedTiles[i].y + terrainTile.rect.y - brushRect.y + terrainTile.writeOffset.y, - clippedTiles[i].width, - clippedTiles[i].height); - - var readRect = new Rect( - writeRect.x / (float)brushRect.width, - writeRect.y / (float)brushRect.height, - writeRect.width / (float)brushRect.width, - writeRect.height / (float)brushRect.height); - - destinationRenderTexture.filterMode = FilterMode.Point; - - for (int j = 0; j < terrainTile.terrain.terrainData.alphamapTextureCount; j++) - { - Texture2D sourceTex = terrainTile.terrain.terrainData.alphamapTextures[j]; - - int mapIndex = terrainTile.mapIndex; - int channelIndex = terrainTile.channelIndex; - - Rect combineRect = new Rect( - clippedTiles[i].x / (float)sourceTex.width, - clippedTiles[i].y / (float)sourceTex.height, - clippedTiles[i].width / (float)sourceTex.width, - clippedTiles[i].height / (float)sourceTex.height); - - copyTerrainLayerMaterial.SetTexture("_MainTex", destinationRenderTexture); - copyTerrainLayerMaterial.SetTexture("_OldAlphaMapTexture", sourceRenderTexture); - copyTerrainLayerMaterial.SetTexture("_AlphaMapTexture", sourceTex); - copyTerrainLayerMaterial.SetVector("_LayerMask", j == mapIndex ? layerMasks[channelIndex] : Vector4.zero); - copyTerrainLayerMaterial.SetPass(1); - - GL.PushMatrix(); - GL.LoadOrtho(); - GL.LoadPixelMatrix(0, destTarget.width, 0, destTarget.height); - - GL.Begin(GL.QUADS); - GL.Color(new Color(1.0f, 1.0f, 1.0f, 1.0f)); - - GL.MultiTexCoord2(0, readRect.x, readRect.y); - GL.MultiTexCoord2(1, combineRect.x, combineRect.y); - GL.Vertex3(writeRect.x, writeRect.y, 0.0f); - GL.MultiTexCoord2(0, readRect.x, readRect.yMax); - GL.MultiTexCoord2(1, combineRect.x, combineRect.yMax); - GL.Vertex3(writeRect.x, writeRect.yMax, 0.0f); - GL.MultiTexCoord2(0, readRect.xMax, readRect.yMax); - GL.MultiTexCoord2(1, combineRect.xMax, combineRect.yMax); - GL.Vertex3(writeRect.xMax, writeRect.yMax, 0.0f); - GL.MultiTexCoord2(0, readRect.xMax, readRect.y); - GL.MultiTexCoord2(1, combineRect.xMax, combineRect.y); - GL.Vertex3(writeRect.xMax, writeRect.y, 0.0f); - - GL.End(); - GL.PopMatrix(); - - if (paintTextureUsesCopyTexture) - { - var rtdesc2 = new RenderTextureDescriptor(sourceTex.width, sourceTex.height, RenderTextureFormat.ARGB32); - rtdesc2.sRGB = false; - rtdesc2.useMipMap = true; - rtdesc2.autoGenerateMips = false; - var mips = RenderTexture.GetTemporary(rtdesc2); - if (!mips.IsCreated()) - mips.Create(); - - // Composes mip0 in a RT with full mipchain. - Graphics.CopyTexture(sourceTex, 0, 0, mips, 0, 0); - Graphics.CopyTexture(destTarget, 0, 0, writeRect.x, writeRect.y, writeRect.width, writeRect.height, mips, 0, 0, clippedTiles[i].x, clippedTiles[i].y); - mips.GenerateMips(); - - // Copy them into sourceTex. - Graphics.CopyTexture(mips, sourceTex); - - RenderTexture.ReleaseTemporary(mips); - } - else - { - GraphicsDeviceType deviceType = SystemInfo.graphicsDeviceType; - if (deviceType == GraphicsDeviceType.Metal || deviceType == GraphicsDeviceType.OpenGLCore) - sourceTex.ReadPixels(new Rect(writeRect.x, writeRect.y, writeRect.width, writeRect.height), clippedTiles[i].x, clippedTiles[i].y); - else - sourceTex.ReadPixels(new Rect(writeRect.x, destTarget.height - writeRect.y - writeRect.height, writeRect.width, writeRect.height), clippedTiles[i].x, clippedTiles[i].y); - sourceTex.Apply(); - } - } - - RenderTexture.active = null; - RenderTexture.ReleaseTemporary(destTarget); - - OnTerrainPainted(terrainTile, ToolAction.PaintTexture); - } - } + // for example: + // src = alphaUV + // dst = normalUV + // dst.uv = src.u * scales.xy + src.v * scales.zw + offset + // terrainspace.xz = srcOrigin + src.uv * srcSize + // terrainspace.xz = dstOrigin + dst.uv * dstSize + // dstOrigin + dst.uv * dstSize = srcOrigin + src.uv * srcSize + // dst.uv * dstSize = src.uv * srcSize + srcOrigin - dstOrigin + // dst.uv = (src.uv * srcSize + srcOrigin - dstOrigin) / dstSize + // dst.uv = (src.uv * srcSize) / dstSize + (srcOrigin - dstOrigin) / dstSize + // scales.x = srcSize.x / dstSize.x + // scales.yz = 0.0f; + // scales.w = srcSize.y / dstSize.y + // offset.xy = (srcOrigin.xy - dstOrigin.xy) / dstSize.xy + + // paint context origin in terrain space + // (note this is the UV space origin and size, not the mesh origin & size) + float srcOriginX = (src.pixelRect.xMin - 0.5f) * src.pixelSize.x; + float srcOriginZ = (src.pixelRect.yMin - 0.5f) * src.pixelSize.y; + float srcSizeX = (src.pixelRect.width) * src.pixelSize.x; + float srcSizeZ = (src.pixelRect.height) * src.pixelSize.y; + + // paint context origin in terrain space + // (note this is the UV space origin and size, not the mesh origin & size) + float dstOriginX = (dst.pixelRect.xMin - 0.5f) * dst.pixelSize.x; + float dstOriginZ = (dst.pixelRect.yMin - 0.5f) * dst.pixelSize.y; + float dstSizeX = (dst.pixelRect.width) * dst.pixelSize.x; + float dstSizeZ = (dst.pixelRect.height) * dst.pixelSize.y; + + scaleOffset = new Vector4( + srcSizeX / dstSizeX, + srcSizeZ / dstSizeZ, + (srcOriginX - dstOriginX) / dstSizeX, + (srcOriginZ - dstOriginZ) / dstSizeZ + ); } - [Flags] - public enum ToolAction + // this function sets up material properties used by functions provided in TerrainTool.cginc + public static void SetupTerrainToolMaterialProperties( + PaintContext paintContext, + BrushTransform brushXform, // the brush transform to terrain space (of paintContext.originTerrain) + Material material) { - None = 0, - PaintHeightmap = 1 << 0, - PaintTexture = 1 << 1, + // BrushUV = f(terrainSpace.xz) = f(g(pc.uv)) + // f(ts.xy) = ts.x * brushXform.X + ts.y * brushXform.Y + brushXform.Origin + // g(pc.uv) = ts.xz = pcOrigin + pc.uv * pcSize + // f(g(pc.uv)) == (pcOrigin + pc.uv * pcSize).x * brushXform.X + (pcOrigin + pc.uv * pcSize).y * brushXform.Y + brushXform.Origin + // f(g(pc.uv)) == (pcOrigin.x + pc.u * pcSize.x) * brushXform.X + (pcOrigin.y + pc.v * pcSize.y) * brushXform.Y + brushXform.Origin + // f(g(pc.uv)) == (pcOrigin.x * brushXform.X) + (pc.u * pcSize.x) * brushXform.X + (pcOrigin.y * brushXform.Y) + (pc.v * pcSize.y) * brushXform.Y + brushXform.Origin + // f(g(pc.uv)) == pc.u * (pcSize.x * brushXform.X) + pc.v * (pcSize.y * brushXform.Y) + (brushXform.Origin + (pcOrigin.x * brushXform.X) + (pcOrigin.y * brushXform.Y)) + + // pcOrigin = (pc.pixelRect.xyMin - 0.5) * pc.pixelSize.xy + // pcSize = (pc.pixelRect.wh) * pc.pixelSize.xy + + // paint context origin in terrain space + // (note this is the UV space origin and size, not the mesh origin & size) + float pcOriginX = (paintContext.pixelRect.xMin - 0.5f) * paintContext.pixelSize.x; + float pcOriginZ = (paintContext.pixelRect.yMin - 0.5f) * paintContext.pixelSize.y; + float pcSizeX = (paintContext.pixelRect.width) * paintContext.pixelSize.x; + float pcSizeZ = (paintContext.pixelRect.height) * paintContext.pixelSize.y; + + Vector2 scaleU = pcSizeX * brushXform.targetX; + Vector2 scaleV = pcSizeZ * brushXform.targetY; + Vector2 offset = brushXform.targetOrigin + pcOriginX * brushXform.targetX + pcOriginZ * brushXform.targetY; + material.SetVector("_PCUVToBrushUVScales", new Vector4(scaleU.x, scaleU.y, scaleV.x, scaleV.y)); + material.SetVector("_PCUVToBrushUVOffset", new Vector4(offset.x, offset.y, 0.0f, 0.0f)); } - private static bool paintTextureUsesCopyTexture + internal static bool paintTextureUsesCopyTexture { get { @@ -419,12 +125,10 @@ private static bool paintTextureUsesCopyTexture } } - static PaintContext InitializePaintContext(Terrain terrain, Rect bounds, int inputTextureWidth, int inputTextureHeight, RenderTextureFormat colorFormat) + static PaintContext InitializePaintContext(Terrain terrain, Texture target, RenderTextureFormat pcFormat, Rect boundsInTerrainSpace, int extraBorderPixels = 0) { - PaintContext ctx = new PaintContext(); - ctx.CalculateBrushRect(terrain, bounds, inputTextureWidth, inputTextureHeight); - ctx.CreateTerrainTiles(terrain, inputTextureWidth, inputTextureHeight); - ctx.CreateRenderTargets(colorFormat); + PaintContext ctx = PaintContext.CreateFromBounds(terrain, boundsInTerrainSpace, target.width, target.height, extraBorderPixels); + ctx.CreateRenderTargets(pcFormat); return ctx; } @@ -433,77 +137,11 @@ public static void ReleaseContextResources(PaintContext ctx) ctx.Cleanup(); } - public static Rect CalculateBrushRectInTerrainUnits(Terrain terrain, Vector2 uv, float brushSize) - { - Vector3 terrainSize = terrain.terrainData.size; - return new Rect(uv * new Vector2(terrainSize.x, terrainSize.z) - Vector2.one * brushSize * 0.5f, Vector2.one * brushSize); - } - - // Collects modified terrain so that we can update some deferred operations at the mouse up event - private class PaintedTerrain - { - public Terrain terrain; - public ToolAction action; - }; - private static List s_PaintedTerrain = new List(); - - private static void OnTerrainPainted(TerrainTile tile, ToolAction action) - { - for (int i = 0; i < s_PaintedTerrain.Count; ++i) - { - if (tile.terrain == s_PaintedTerrain[i].terrain) - { - s_PaintedTerrain[i].action |= action; - return; - } - } - s_PaintedTerrain.Add(new PaintedTerrain { terrain = tile.terrain, action = action }); - } - - public static void FlushAllPaints() - { - for (int i = 0; i < s_PaintedTerrain.Count; ++i) - { - var pt = s_PaintedTerrain[i]; - if ((pt.action & ToolAction.PaintHeightmap) != 0) - { - pt.terrain.ApplyDelayedHeightmapModification(); - } - if ((pt.action & ToolAction.PaintTexture) != 0) - { - var terrainData = pt.terrain.terrainData; - if (terrainData == null) - continue; - terrainData.SetBaseMapDirty(); - if (paintTextureUsesCopyTexture) - { - // pull the data from GPU to CPU - var rtdesc = new RenderTextureDescriptor(terrainData.alphamapResolution, terrainData.alphamapResolution, RenderTextureFormat.ARGB32); - rtdesc.sRGB = false; - rtdesc.useMipMap = false; - rtdesc.autoGenerateMips = false; - RenderTexture tmp = RenderTexture.GetTemporary(rtdesc); - for (int c = 0; c < terrainData.alphamapTextureCount; ++c) - { - Graphics.Blit(terrainData.alphamapTextures[c], tmp); - terrainData.alphamapTextures[c].ReadPixels(new Rect(0, 0, rtdesc.width, rtdesc.height), 0, 0, true); - } - RenderTexture.ReleaseTemporary(tmp); - } - } - } - - s_PaintedTerrain.Clear(); - } - - // TerrainPaintUtilityEditor hooks to this event to do automatic undo - internal static event Action onTerrainTileBeforePaint; - - public static PaintContext BeginPaintHeightmap(Terrain terrain, Rect bounds) // bounds in terrain space units + public static PaintContext BeginPaintHeightmap(Terrain terrain, Rect boundsInTerrainSpace, int extraBorderPixels = 0) { RenderTexture rt = terrain.terrainData.heightmapTexture; - PaintContext ctx = InitializePaintContext(terrain, bounds, rt.width, rt.height, rt.format); - ctx.GatherHeightmap(terrain); + PaintContext ctx = InitializePaintContext(terrain, rt, rt.format, boundsInTerrainSpace, extraBorderPixels); + ctx.GatherHeightmap(); return ctx; } @@ -513,15 +151,15 @@ public static void EndPaintHeightmap(PaintContext ctx, string editorUndoName) ctx.Cleanup(); } - public static PaintContext CollectNormals(Terrain terrain, Rect bounds) + public static PaintContext CollectNormals(Terrain terrain, Rect boundsInTerrainSpace, int extraBorderPixels = 0) { RenderTexture rt = terrain.normalmapTexture; - PaintContext ctx = InitializePaintContext(terrain, bounds, rt.width, rt.height, rt.format); - ctx.GatherNormals(terrain); + PaintContext ctx = InitializePaintContext(terrain, rt, rt.format, boundsInTerrainSpace, extraBorderPixels); + ctx.GatherNormals(); return ctx; } - public static PaintContext BeginPaintTexture(Terrain terrain, Rect bounds, TerrainLayer inputLayer) + public static PaintContext BeginPaintTexture(Terrain terrain, Rect boundsInTerrainSpace, TerrainLayer inputLayer, int extraBorderPixels = 0) { if (inputLayer == null) return null; @@ -532,8 +170,8 @@ public static PaintContext BeginPaintTexture(Terrain terrain, Rect bounds, Terra Texture2D inputTexture = GetTerrainAlphaMapChecked(terrain, terrainLayerIndex >> 2); - PaintContext ctx = InitializePaintContext(terrain, bounds, inputTexture.width, inputTexture.height, RenderTextureFormat.R8); - ctx.GatherAlphamap(terrain, inputLayer); + PaintContext ctx = InitializePaintContext(terrain, inputTexture, RenderTextureFormat.R8, boundsInTerrainSpace, extraBorderPixels); + ctx.GatherAlphamap(inputLayer); return ctx; } @@ -560,161 +198,39 @@ public static Material GetCopyTerrainLayerMaterial() return m_CopyTerrainLayerMaterial; } - static void DrawQuad(int width, int height, Rect source, Rect destination) - { - GL.PushMatrix(); - GL.LoadOrtho(); - GL.LoadPixelMatrix(0, width, 0, height); - - GL.Begin(GL.QUADS); - GL.Color(new Color(1.0f, 1.0f, 1.0f, 1.0f)); - - GL.TexCoord2(source.x, source.y); - GL.Vertex3(destination.x, destination.y, 0.0f); - GL.TexCoord2(source.x, source.yMax); - GL.Vertex3(destination.x, destination.yMax, 0.0f); - GL.TexCoord2(source.xMax, source.yMax); - GL.Vertex3(destination.xMax, destination.yMax, 0.0f); - GL.TexCoord2(source.xMax, source.y); - GL.Vertex3(destination.xMax, destination.y, 0.0f); - - GL.End(); - GL.PopMatrix(); - } - - public static RectInt CalcBrushRectInPixels(Terrain terrain, Rect brushRect, int textureWidth, int textureHeight) - { - int xMin = Mathf.FloorToInt(((float)textureWidth) * brushRect.xMin / terrain.terrainData.size.x); - int yMin = Mathf.FloorToInt(((float)textureHeight) * brushRect.yMin / terrain.terrainData.size.z); - int xMax = Mathf.CeilToInt(((float)textureWidth) * brushRect.xMax / terrain.terrainData.size.x); - int yMax = Mathf.CeilToInt(((float)textureHeight) * brushRect.yMax / terrain.terrainData.size.z); - return new RectInt(xMin, yMin, xMax - xMin, yMax - yMin); - } - - public static TerrainTile[] FindTerrainTiles(Terrain terrain, int width, int height, RectInt brushRect) + internal static void DrawQuad(RectInt destinationPixels, RectInt sourcePixels, Texture sourceTexture) { - List terrainTiles = new List(); - - Terrain left = terrain.leftNeighbor; - Terrain right = terrain.rightNeighbor; - Terrain top = terrain.topNeighbor; - Terrain bottom = terrain.bottomNeighbor; - - bool wantLeft = (brushRect.x < 0); - bool wantRight = (brushRect.xMax > (width - 1)); - bool wantTop = (brushRect.yMax > (height - 1)); - bool wantBottom = (brushRect.y < 0); - - if (wantLeft && wantRight) + if ((destinationPixels.width > 0) && (destinationPixels.height > 0)) { - Debug.Log("FindTerrainTiles query rectangle too large!"); - wantRight = false; + Rect sourceUVs = new Rect( + (sourcePixels.x) / (float)sourceTexture.width, + (sourcePixels.y) / (float)sourceTexture.height, + (sourcePixels.width) / (float)sourceTexture.width, + (sourcePixels.height) / (float)sourceTexture.height); + + GL.Begin(GL.QUADS); + GL.Color(new Color(1.0f, 1.0f, 1.0f, 1.0f)); + GL.TexCoord2(sourceUVs.x, sourceUVs.y); + GL.Vertex3(destinationPixels.x, destinationPixels.y, 0.0f); + GL.TexCoord2(sourceUVs.x, sourceUVs.yMax); + GL.Vertex3(destinationPixels.x, destinationPixels.yMax, 0.0f); + GL.TexCoord2(sourceUVs.xMax, sourceUVs.yMax); + GL.Vertex3(destinationPixels.xMax, destinationPixels.yMax, 0.0f); + GL.TexCoord2(sourceUVs.xMax, sourceUVs.y); + GL.Vertex3(destinationPixels.xMax, destinationPixels.y, 0.0f); + GL.End(); } - - if (wantTop && wantBottom) - { - Debug.Log("FindTerrainTiles query rectangle too large!"); - wantBottom = false; - } - - // add center tile - TerrainTile tile = new TerrainTile(terrain, new RectInt(0, 0, width, height)); - tile.readOffset = Vector2Int.zero; - tile.writeOffset = Vector2Int.zero; - terrainTiles.Add(tile); - - // add horizontal and vertical neighbors - Terrain horiz = null; - Terrain vert = null; - Terrain cornerTerrain = null; - - int xBias = 0; - int yBias = 0; - int xReadBias = 0; - int yReadBias = 0; - int xWriteBias = 0; - int yWriteBias = 0; - - if (wantLeft) - { - xBias = -1; - xReadBias = -1; - xWriteBias = 1; - horiz = left; - } - else if (wantRight) - { - xBias = 1; - xReadBias = 1; - xWriteBias = -1; - horiz = right; - } - - if (wantTop) - { - yBias = 1; - yReadBias = 1; - yWriteBias = -1; - vert = top; - } - else if (wantBottom) - { - yBias = -1; - yReadBias = -1; - yWriteBias = 1; - vert = bottom; - } - - if (horiz) - { - tile = new TerrainTile(horiz, new RectInt(xBias * width, 0, width, height)); - tile.readOffset = new Vector2Int(xReadBias, 0); - tile.writeOffset = new Vector2Int(xWriteBias, 0); - terrainTiles.Add(tile); - - // add corner, if we have a link - if (wantTop && horiz.topNeighbor) - cornerTerrain = horiz.topNeighbor; - else if (wantBottom && horiz.bottomNeighbor) - cornerTerrain = horiz.bottomNeighbor; - } - - if (vert) - { - tile = new TerrainTile(vert, new RectInt(0, yBias * height, width, height)); - tile.readOffset = new Vector2Int(0, yReadBias); - tile.writeOffset = new Vector2Int(0, yWriteBias); - terrainTiles.Add(tile); - - // add corner, if we have a link - if (wantLeft && vert.leftNeighbor) - cornerTerrain = vert.leftNeighbor; - else if (wantRight && vert.rightNeighbor) - cornerTerrain = vert.rightNeighbor; - } - - if (cornerTerrain != null) - { - tile = new TerrainTile(cornerTerrain, new RectInt(xBias * width, yBias * height, width, height)); - tile.readOffset = new Vector2Int(xReadBias, yReadBias); - tile.writeOffset = new Vector2Int(xWriteBias, yWriteBias); - terrainTiles.Add(tile); - } - - return terrainTiles.ToArray(); } - public static RectInt[] ClipTerrainTiles(TerrainTile[] terrainTiles, RectInt brushRect) + internal static RectInt CalcPixelRectFromBounds(Terrain terrain, Rect boundsInTerrainSpace, int textureWidth, int textureHeight, int extraBorderPixels) { - RectInt[] clippedTiles = new RectInt[terrainTiles.Length]; - for (int i = 0; i < terrainTiles.Length; i++) - { - clippedTiles[i].x = Mathf.Max(0, brushRect.x - terrainTiles[i].rect.x); - clippedTiles[i].y = Mathf.Max(0, brushRect.y - terrainTiles[i].rect.y); - clippedTiles[i].xMax = Mathf.Min(terrainTiles[i].rect.width, brushRect.xMax - terrainTiles[i].rect.x); - clippedTiles[i].yMax = Mathf.Min(terrainTiles[i].rect.height, brushRect.yMax - terrainTiles[i].rect.y); - } - return clippedTiles; + float scaleX = (textureWidth - 1.0f) / terrain.terrainData.size.x; + float scaleY = (textureHeight - 1.0f) / terrain.terrainData.size.z; + int xMin = Mathf.FloorToInt(boundsInTerrainSpace.xMin * scaleX) - extraBorderPixels; + int yMin = Mathf.FloorToInt(boundsInTerrainSpace.yMin * scaleY) - extraBorderPixels; + int xMax = Mathf.CeilToInt(boundsInTerrainSpace.xMax * scaleX) + extraBorderPixels; + int yMax = Mathf.CeilToInt(boundsInTerrainSpace.yMax * scaleY) + extraBorderPixels; + return new RectInt(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); } // Alphamap utilities @@ -736,7 +252,7 @@ static public int FindTerrainLayerIndex(Terrain terrain, TerrainLayer inputLayer return -1; } - static int AddTerrainLayer(Terrain terrain, TerrainLayer inputLayer) + internal static int AddTerrainLayer(Terrain terrain, TerrainLayer inputLayer) { int newIndex = terrain.terrainData.terrainLayers.Length; var newarray = new TerrainLayer[newIndex + 1]; diff --git a/Modules/TerrainEditor/Brush/Brush.cs b/Modules/TerrainEditor/Brush/Brush.cs index 8453538495..a300ec29ac 100644 --- a/Modules/TerrainEditor/Brush/Brush.cs +++ b/Modules/TerrainEditor/Brush/Brush.cs @@ -99,7 +99,7 @@ internal static Texture2D GenerateBrushTexture(Texture2D mask, AnimationCurve fa s_CreateBrushMaterial = new Material(EditorGUIUtility.LoadRequired("Brushes/CreateBrush.shader") as Shader); int sampleCount = Mathf.Max(width, 1024); - Texture2D falloffTex = new Texture2D(sampleCount, 1, TextureFormat.R8, false); + Texture2D falloffTex = new Texture2D(sampleCount, 1, TextureFormat.R16, false); Color[] falloffPix = new Color[sampleCount]; for (int i = 0; i < sampleCount; i++) { diff --git a/Modules/TerrainEditor/Brush/BrushList.cs b/Modules/TerrainEditor/Brush/BrushList.cs index 7e199ba0ed..553ef2891d 100644 --- a/Modules/TerrainEditor/Brush/BrushList.cs +++ b/Modules/TerrainEditor/Brush/BrushList.cs @@ -97,6 +97,11 @@ public void UpdateSelection(int newSelectedBrush) m_BrushEditor = Editor.CreateEditor(GetActiveBrush()); } + public Brush GetCircleBrush() + { + return m_BrushList[0]; + } + public Brush GetActiveBrush() { if (m_SelectedBrush >= m_BrushList.Length) diff --git a/Modules/TerrainEditor/PaintTools/PaintHeightTool.cs b/Modules/TerrainEditor/PaintTools/PaintHeightTool.cs index eef201b7d3..3770163db0 100644 --- a/Modules/TerrainEditor/PaintTools/PaintHeightTool.cs +++ b/Modules/TerrainEditor/PaintTools/PaintHeightTool.cs @@ -25,25 +25,56 @@ public override void OnInspectorGUI(Terrain terrain, IOnInspectorGUI editContext editContext.ShowBrushesGUI(5); } - public override void OnSceneGUI(Terrain terrain, IOnSceneGUI editContext) + private void ApplyBrushInternal(PaintContext paintContext, float brushStrength, Texture brushTexture, BrushTransform brushXform) { - TerrainPaintUtilityEditor.ShowDefaultPreviewBrush(terrain, editContext.brushTexture, editContext.brushStrength * 0.01f, editContext.brushSize, editContext.brushStrength * 0.01f); + Material mat = TerrainPaintUtility.GetBuiltinPaintMaterial(); + + brushStrength = Event.current.shift ? -brushStrength : brushStrength; + Vector4 brushParams = new Vector4(0.01f * brushStrength, 0.0f, 0.0f, 0.0f); + mat.SetTexture("_BrushTex", brushTexture); + mat.SetVector("_BrushParams", brushParams); + + TerrainPaintUtility.SetupTerrainToolMaterialProperties(paintContext, brushXform, mat); + + Graphics.Blit(paintContext.sourceRenderTexture, paintContext.destinationRenderTexture, mat, (int)TerrainPaintUtility.BuiltinPaintMaterialPasses.RaiseLowerHeight); } - public override bool OnPaint(Terrain terrain, IOnPaint editContext) + public override void OnSceneGUI(Terrain terrain, IOnSceneGUI editContext) { - float brushStrength = Event.current.shift ? -editContext.brushStrength : editContext.brushStrength; + // We're only doing painting operations, early out if it's not a repaint + if (Event.current.type != EventType.Repaint) + return; - Material mat = TerrainPaintUtility.GetBuiltinPaintMaterial(); - Rect brushRect = TerrainPaintUtility.CalculateBrushRectInTerrainUnits(terrain, editContext.uv, editContext.brushSize); - TerrainPaintUtility.PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushRect); + if (editContext.hitValidTerrain) + { + BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.raycastHit.textureCoord, editContext.brushSize, 0.0f); + PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds(), 1); - // apply brush - Vector4 brushParams = new Vector4(brushStrength * 0.01f, 0.0f, 0.0f, 0.0f); - mat.SetTexture("_BrushTex", editContext.brushTexture); - mat.SetVector("_BrushParams", brushParams); - Graphics.Blit(paintContext.sourceRenderTexture, paintContext.destinationRenderTexture, mat, (int)TerrainPaintUtility.BuiltinPaintMaterialPasses.RaiseLowerHeight); + Material material = TerrainPaintUtilityEditor.GetDefaultBrushPreviewMaterial(); + TerrainPaintUtilityEditor.DrawBrushPreview( + paintContext, TerrainPaintUtilityEditor.BrushPreview.SourceRenderTexture, editContext.brushTexture, brushXform, material, 0); + + // draw result preview + { + ApplyBrushInternal(paintContext, editContext.brushStrength, editContext.brushTexture, brushXform); + + // restore old render target + RenderTexture.active = paintContext.oldRenderTexture; + + material.SetTexture("_HeightmapOrig", paintContext.sourceRenderTexture); + TerrainPaintUtilityEditor.DrawBrushPreview( + paintContext, TerrainPaintUtilityEditor.BrushPreview.DestinationRenderTexture, editContext.brushTexture, brushXform, material, 1); + } + TerrainPaintUtility.ReleaseContextResources(paintContext); + } + } + + public override bool OnPaint(Terrain terrain, IOnPaint editContext) + { + BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.uv, editContext.brushSize, 0.0f); + PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds()); + ApplyBrushInternal(paintContext, editContext.brushStrength, editContext.brushTexture, brushXform); TerrainPaintUtility.EndPaintHeightmap(paintContext, "Terrain Paint - Raise or Lower Height"); return true; } diff --git a/Modules/TerrainEditor/PaintTools/PaintTextureTool.cs b/Modules/TerrainEditor/PaintTools/PaintTextureTool.cs index 827487c55e..15e7b57660 100644 --- a/Modules/TerrainEditor/PaintTools/PaintTextureTool.cs +++ b/Modules/TerrainEditor/PaintTools/PaintTextureTool.cs @@ -22,8 +22,6 @@ public class PaintTextureTool : TerrainPaintTool [SerializeField] bool m_ShowLayerEditor = false; - [SerializeField] - float m_SplatAlpha = 1.0f; public override string GetName() { return "Paint Texture"; @@ -36,17 +34,21 @@ public override string GetDesc() public override bool OnPaint(Terrain terrain, IOnPaint editContext) { - Rect brushRect = TerrainPaintUtility.CalculateBrushRectInTerrainUnits(terrain, editContext.uv, editContext.brushSize); - - TerrainPaintUtility.PaintContext paintContext = TerrainPaintUtility.BeginPaintTexture(terrain, brushRect, m_SelectedTerrainLayer); + BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.uv, editContext.brushSize, 0.0f); + PaintContext paintContext = TerrainPaintUtility.BeginPaintTexture(terrain, brushXform.GetBrushXYBounds(), m_SelectedTerrainLayer); if (paintContext == null) return false; Material mat = TerrainPaintUtility.GetBuiltinPaintMaterial(); + // apply brush - Vector4 brushParams = new Vector4(editContext.brushStrength, m_SplatAlpha, 0.0f, 0.0f); + float targetAlpha = 1.0f; // always 1.0 now -- no subtractive painting (we assume this in the ScatterAlphaMap) + Vector4 brushParams = new Vector4(editContext.brushStrength, targetAlpha, 0.0f, 0.0f); mat.SetTexture("_BrushTex", editContext.brushTexture); mat.SetVector("_BrushParams", brushParams); + + TerrainPaintUtility.SetupTerrainToolMaterialProperties(paintContext, brushXform, mat); + Graphics.Blit(paintContext.sourceRenderTexture, paintContext.destinationRenderTexture, mat, (int)TerrainPaintUtility.BuiltinPaintMaterialPasses.PaintTexture); TerrainPaintUtility.EndPaintTexture(paintContext, "Terrain Paint - Texture"); @@ -55,7 +57,17 @@ public override bool OnPaint(Terrain terrain, IOnPaint editContext) public override void OnSceneGUI(Terrain terrain, IOnSceneGUI editContext) { - TerrainPaintUtilityEditor.ShowDefaultPreviewBrush(terrain, editContext.brushTexture, editContext.brushStrength, editContext.brushSize, 0.0f); + // We're only doing painting operations, early out if it's not a repaint + if (Event.current.type != EventType.Repaint) + return; + + if (editContext.hitValidTerrain) + { + BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.raycastHit.textureCoord, editContext.brushSize, 0.0f); + PaintContext ctx = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds(), 1); + TerrainPaintUtilityEditor.DrawBrushPreview(ctx, TerrainPaintUtilityEditor.BrushPreview.SourceRenderTexture, editContext.brushTexture, brushXform, TerrainPaintUtilityEditor.GetDefaultBrushPreviewMaterial(), 0); + TerrainPaintUtility.ReleaseContextResources(ctx); + } } private void DrawFoldoutEditor(Editor editor, int controlId, ref bool visible) @@ -89,7 +101,6 @@ public override void OnInspectorGUI(Terrain terrain, IOnInspectorGUI editContext GUILayout.Label("Settings", EditorStyles.boldLabel); EditorGUI.BeginChangeCheck(); - m_SplatAlpha = EditorGUILayout.Slider("Target Strength", m_SplatAlpha, 0.0F, 1.0F); EditorGUILayout.Space(); if (m_TemplateMaterialEditor != null && m_TemplateMaterialEditor.target != terrain.materialTemplate) diff --git a/Modules/TerrainEditor/PaintTools/SetHeightTool.cs b/Modules/TerrainEditor/PaintTools/SetHeightTool.cs index a2d3999f35..2d3087b176 100644 --- a/Modules/TerrainEditor/PaintTools/SetHeightTool.cs +++ b/Modules/TerrainEditor/PaintTools/SetHeightTool.cs @@ -29,7 +29,48 @@ public override string GetDesc() public override void OnSceneGUI(Terrain terrain, IOnSceneGUI editContext) { - TerrainPaintUtilityEditor.ShowDefaultPreviewBrush(terrain, editContext.brushTexture, editContext.brushStrength * 0.01f, editContext.brushSize, 0); + // We're only doing painting operations, early out if it's not a repaint + if (Event.current.type != EventType.Repaint) + return; + + if (editContext.hitValidTerrain) + { + BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.raycastHit.textureCoord, editContext.brushSize, 0.0f); + PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds(), 1); + + Material material = TerrainPaintUtilityEditor.GetDefaultBrushPreviewMaterial(); + + TerrainPaintUtilityEditor.DrawBrushPreview( + paintContext, TerrainPaintUtilityEditor.BrushPreview.SourceRenderTexture, editContext.brushTexture, brushXform, material, 0); + + // draw result preview + { + ApplyBrushInternal(paintContext, editContext.brushStrength, editContext.brushTexture, brushXform); + + // restore old render target + RenderTexture.active = paintContext.oldRenderTexture; + + material.SetTexture("_HeightmapOrig", paintContext.sourceRenderTexture); + + TerrainPaintUtilityEditor.DrawBrushPreview( + paintContext, TerrainPaintUtilityEditor.BrushPreview.DestinationRenderTexture, editContext.brushTexture, brushXform, material, 1); + } + + TerrainPaintUtility.ReleaseContextResources(paintContext); + } + } + + private void ApplyBrushInternal(PaintContext paintContext, float brushStrength, Texture brushTexture, BrushTransform brushXform) + { + Material mat = TerrainPaintUtility.GetBuiltinPaintMaterial(); + + Vector4 brushParams = new Vector4(brushStrength * 0.01f, 0.5f * m_Height, 0.0f, 0.0f); + mat.SetTexture("_BrushTex", brushTexture); + mat.SetVector("_BrushParams", brushParams); + + TerrainPaintUtility.SetupTerrainToolMaterialProperties(paintContext, brushXform, mat); + + Graphics.Blit(paintContext.sourceRenderTexture, paintContext.destinationRenderTexture, mat, (int)TerrainPaintUtility.BuiltinPaintMaterialPasses.SetHeights); } public override bool OnPaint(Terrain terrain, IOnPaint editContext) @@ -40,17 +81,10 @@ public override bool OnPaint(Terrain terrain, IOnPaint editContext) editContext.RepaintAllInspectors(); return true; } - Material mat = TerrainPaintUtility.GetBuiltinPaintMaterial(); - - Rect brushRect = TerrainPaintUtility.CalculateBrushRectInTerrainUnits(terrain, editContext.uv, editContext.brushSize); - TerrainPaintUtility.PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushRect); - - Vector4 brushParams = new Vector4(editContext.brushStrength * 0.01f, 0.5f * m_Height, 0.0f, 0.0f); - mat.SetTexture("_BrushTex", editContext.brushTexture); - mat.SetVector("_BrushParams", brushParams); - - Graphics.Blit(paintContext.sourceRenderTexture, paintContext.destinationRenderTexture, mat, (int)TerrainPaintUtility.BuiltinPaintMaterialPasses.SetHeights); + BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.uv, editContext.brushSize, 0.0f); + PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds()); + ApplyBrushInternal(paintContext, editContext.brushStrength, editContext.brushTexture, brushXform); TerrainPaintUtility.EndPaintHeightmap(paintContext, "Terrain Paint - Set Height"); return true; } diff --git a/Modules/TerrainEditor/PaintTools/SmoothHeightTool.cs b/Modules/TerrainEditor/PaintTools/SmoothHeightTool.cs index d345368f35..ed607efb85 100644 --- a/Modules/TerrainEditor/PaintTools/SmoothHeightTool.cs +++ b/Modules/TerrainEditor/PaintTools/SmoothHeightTool.cs @@ -25,22 +25,41 @@ public override void OnInspectorGUI(Terrain terrain, IOnInspectorGUI editContext editContext.ShowBrushesGUI(5); } - public override void OnSceneGUI(Terrain terrain, IOnSceneGUI editContext) + private void ApplyBrushInternal(PaintContext paintContext, float brushStrength, Texture brushTexture, BrushTransform brushXform) { - TerrainPaintUtilityEditor.ShowDefaultPreviewBrush(terrain, editContext.brushTexture, editContext.brushStrength, editContext.brushSize, 0.0f); - } + Material mat = TerrainPaintUtility.GetBuiltinPaintMaterial(); - public override bool OnPaint(Terrain terrain, IOnPaint editContext) - { - Rect brushRect = TerrainPaintUtility.CalculateBrushRectInTerrainUnits(terrain, editContext.uv, editContext.brushSize); - TerrainPaintUtility.PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushRect); + brushStrength = Event.current.shift ? -brushStrength : brushStrength; - Material mat = TerrainPaintUtility.GetBuiltinPaintMaterial(); - Vector4 brushParams = new Vector4(editContext.brushStrength, 0.0f, 0.0f, 0.0f); - mat.SetTexture("_BrushTex", editContext.brushTexture); + Vector4 brushParams = new Vector4(brushStrength, 0.0f, 0.0f, 0.0f); + mat.SetTexture("_BrushTex", brushTexture); mat.SetVector("_BrushParams", brushParams); + + TerrainPaintUtility.SetupTerrainToolMaterialProperties(paintContext, brushXform, mat); + Graphics.Blit(paintContext.sourceRenderTexture, paintContext.destinationRenderTexture, mat, (int)TerrainPaintUtility.BuiltinPaintMaterialPasses.SmoothHeights); + } + + public override void OnSceneGUI(Terrain terrain, IOnSceneGUI editContext) + { + // We're only doing painting operations, early out if it's not a repaint + if (Event.current.type != EventType.Repaint) + return; + if (editContext.hitValidTerrain) + { + BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.raycastHit.textureCoord, editContext.brushSize, 0.0f); + PaintContext ctx = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds(), 1); + TerrainPaintUtilityEditor.DrawBrushPreview(ctx, TerrainPaintUtilityEditor.BrushPreview.SourceRenderTexture, editContext.brushTexture, brushXform, TerrainPaintUtilityEditor.GetDefaultBrushPreviewMaterial(), 0); + TerrainPaintUtility.ReleaseContextResources(ctx); + } + } + + public override bool OnPaint(Terrain terrain, IOnPaint editContext) + { + BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.uv, editContext.brushSize, 0.0f); + PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds()); + ApplyBrushInternal(paintContext, editContext.brushStrength, editContext.brushTexture, brushXform); TerrainPaintUtility.EndPaintHeightmap(paintContext, "Terrain Paint - Smooth Height"); return true; } diff --git a/Modules/TerrainEditor/PaintTools/StampTool.cs b/Modules/TerrainEditor/PaintTools/StampTool.cs index f4f4eafa62..d6d59e57da 100644 --- a/Modules/TerrainEditor/PaintTools/StampTool.cs +++ b/Modules/TerrainEditor/PaintTools/StampTool.cs @@ -21,45 +21,84 @@ public override string GetName() public override string GetDesc() { - return "Left click to stamp the brush onto the terrain.\n\nHold shift and left click to stamp negative."; + return "Left click to stamp the brush onto the terrain.\n\nHold shift and mousewheel to adjust height."; } - public override bool OnPaint(Terrain terrain, IOnPaint editContext) + private void ApplyBrushInternal(PaintContext paintContext, float brushStrength, Texture brushTexture, BrushTransform brushXform) { - if (Event.current.type == EventType.MouseDrag) - return true; - Material mat = TerrainPaintUtility.GetBuiltinPaintMaterial(); - Rect brushRect = TerrainPaintUtility.CalculateBrushRectInTerrainUnits(terrain, editContext.uv, editContext.brushSize); - TerrainPaintUtility.PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushRect); - - Vector4 brushParams = new Vector4(editContext.brushStrength * 0.01f, 0.0f, m_StampHeight, 0.0f); + Vector4 brushParams = new Vector4(0.01f * brushStrength, 0.0f, m_StampHeight, 0.0f); + mat.SetTexture("_BrushTex", brushTexture); + mat.SetVector("_BrushParams", brushParams); - if (Event.current.shift) - brushParams.x = -brushParams.x; + TerrainPaintUtility.SetupTerrainToolMaterialProperties(paintContext, brushXform, mat); - mat.SetTexture("_BrushTex", editContext.brushTexture); - mat.SetVector("_BrushParams", brushParams); Graphics.Blit(paintContext.sourceRenderTexture, paintContext.destinationRenderTexture, mat, (int)TerrainPaintUtility.BuiltinPaintMaterialPasses.StampHeight); + } + + public override bool OnPaint(Terrain terrain, IOnPaint editContext) + { + // ignore mouse drags + if (Event.current.type == EventType.MouseDrag) + return true; + BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.uv, editContext.brushSize, 0.0f); + PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds()); + ApplyBrushInternal(paintContext, editContext.brushStrength, editContext.brushTexture, brushXform); TerrainPaintUtility.EndPaintHeightmap(paintContext, "Terrain Paint - Stamp"); return true; } public override void OnSceneGUI(Terrain terrain, IOnSceneGUI editContext) { - TerrainPaintUtilityEditor.ShowDefaultPreviewBrush(terrain, editContext.brushTexture, editContext.brushStrength * 0.01f, editContext.brushSize, m_StampHeight); + Event evt = Event.current; + if (evt.shift && (evt.type == EventType.ScrollWheel)) + { + m_StampHeight += Event.current.delta.y * -0.0000007f * editContext.raycastHit.distance; + evt.Use(); + } + + // We're only doing painting operations, early out if it's not a repaint + if (evt.type != EventType.Repaint) + return; + + if (editContext.hitValidTerrain) + { + BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, editContext.raycastHit.textureCoord, editContext.brushSize, 0.0f); + PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds(), 1); + + Material material = TerrainPaintUtilityEditor.GetDefaultBrushPreviewMaterial(); + + TerrainPaintUtilityEditor.DrawBrushPreview( + paintContext, TerrainPaintUtilityEditor.BrushPreview.SourceRenderTexture, editContext.brushTexture, brushXform, material, 0); + + // draw result preview + { + ApplyBrushInternal(paintContext, editContext.brushStrength, editContext.brushTexture, brushXform); + + // restore old render target + RenderTexture.active = paintContext.oldRenderTexture; + + material.SetTexture("_HeightmapOrig", paintContext.sourceRenderTexture); + + TerrainPaintUtilityEditor.DrawBrushPreview( + paintContext, TerrainPaintUtilityEditor.BrushPreview.DestinationRenderTexture, editContext.brushTexture, brushXform, material, 1); + } + + TerrainPaintUtility.ReleaseContextResources(paintContext); + } } public override void OnInspectorGUI(Terrain terrain, IOnInspectorGUI editContext) { EditorGUI.BeginChangeCheck(); - m_StampHeight = EditorGUILayout.Slider(new GUIContent("Stamp Height", "You can set the Stamp Height property manually or you can shift-click on the terrain to sample the height at the mouse position (rather like the “eyedropper” tool in an image editor)."), m_StampHeight * terrain.terrainData.size.y, 0, terrain.terrainData.size.y) / terrain.terrainData.size.y; + m_StampHeight = EditorGUILayout.Slider(new GUIContent("Stamp Height", "You can set the Stamp Height manually or you can hold shift and mouse wheel on the terrain to adjust it."), m_StampHeight * terrain.terrainData.size.y, 0, terrain.terrainData.size.y) / terrain.terrainData.size.y; if (EditorGUI.EndChangeCheck()) Save(true); // show built-in brushes + editContext.ShowBrushesGUI(5); base.OnInspectorGUI(terrain, editContext); } } diff --git a/Modules/TerrainEditor/PaintTools/TerrainPaintTool.cs b/Modules/TerrainEditor/PaintTools/TerrainPaintTool.cs index 65eb147cdc..9f1de28910 100644 --- a/Modules/TerrainEditor/PaintTools/TerrainPaintTool.cs +++ b/Modules/TerrainEditor/PaintTools/TerrainPaintTool.cs @@ -15,7 +15,6 @@ public interface IOnPaint Texture brushTexture { get; } Vector2 uv { get; } float brushStrength { get; } - float brushRotation { get; } float brushSize { get; } void RepaintAllInspectors(); @@ -25,8 +24,9 @@ public interface IOnSceneGUI SceneView sceneView { get; } Texture brushTexture { get; } float brushStrength { get; } - float brushRotation { get; } float brushSize { get; } + bool hitValidTerrain { get; } + RaycastHit raycastHit { get; } } public interface IOnInspectorGUI @@ -40,6 +40,8 @@ internal interface ITerrainPaintTool string GetDesc(); void OnEnable(); void OnDisable(); + void OnEnterToolMode(); + void OnExitToolMode(); void OnSceneGUI(Terrain terrain, IOnSceneGUI editContext); void OnInspectorGUI(Terrain terrain, IOnInspectorGUI editContext); bool OnPaint(Terrain terrain, IOnPaint editContext); @@ -51,6 +53,8 @@ public abstract class TerrainPaintTool : ScriptableSingleton, ITerrainPain public abstract string GetDesc(); public virtual void OnEnable() {} public virtual void OnDisable() {} + public virtual void OnEnterToolMode() {} + public virtual void OnExitToolMode() {} public virtual void OnSceneGUI(Terrain terrain, IOnSceneGUI editContext) {} public virtual void OnInspectorGUI(Terrain terrain, IOnInspectorGUI editContext) {} public virtual bool OnPaint(Terrain terrain, IOnPaint editContext) { return false; } @@ -62,27 +66,28 @@ internal class OnPaintContext : IOnPaint internal Vector2 m_UV = Vector2.zero; internal float m_BrushStrength = 0.0f; internal float m_BrushSize = 0; - internal float m_BrushRotation = 0.0f; + internal bool m_HitValidTerrain = false; + internal RaycastHit m_RaycastHit; - public OnPaintContext(Texture brushTexture, Vector2 uv, float brushStrength, float brushRotation, float brushSize) + public OnPaintContext(RaycastHit raycastHit, Texture brushTexture, Vector2 uv, float brushStrength, float brushSize) { - Set(brushTexture, uv, brushStrength, brushRotation, brushSize); + Set(false, raycastHit, brushTexture, uv, brushStrength, brushSize); } - public OnPaintContext Set(Texture brushTexture, Vector2 uv, float brushStrength, float brushRotation, float brushSize) + public OnPaintContext Set(bool hitValidTerrain, RaycastHit raycastHit, Texture brushTexture, Vector2 uv, float brushStrength, float brushSize) { m_BrushTexture = brushTexture; m_UV = uv; m_BrushStrength = brushStrength; m_BrushSize = brushSize; - m_BrushRotation = brushRotation; + m_HitValidTerrain = hitValidTerrain; + m_RaycastHit = raycastHit; return this; } public Texture brushTexture { get { return m_BrushTexture; } } public Vector2 uv { get { return m_UV; } } public float brushStrength { get { return m_BrushStrength; } } - public float brushRotation { get { return m_BrushRotation; } } public float brushSize { get { return m_BrushSize; } } public void RepaintAllInspectors() { InspectorWindow.RepaintAllInspectors(); } @@ -94,28 +99,31 @@ internal class OnSceneGUIContext : IOnSceneGUI internal Texture m_BrushTexture = null; internal float m_BrushStrength = 0.0f; internal float m_BrushSize = 0; - internal float m_BrushRotation = 0.0f; + internal bool m_HitValidTerrain = false; + internal RaycastHit m_RaycastHit; - public OnSceneGUIContext(SceneView sceneView, Texture brushTexture, float brushStrength, float brushRotation, float brushSize) + public OnSceneGUIContext(SceneView sceneView, RaycastHit raycastHit, Texture brushTexture, float brushStrength, float brushSize) { - Set(sceneView, brushTexture, brushStrength, brushSize, brushRotation); + Set(sceneView, false, raycastHit, brushTexture, brushStrength, brushSize); } - public OnSceneGUIContext Set(SceneView sceneView, Texture brushTexture, float brushStrength, float brushRotation, float brushSize) + public OnSceneGUIContext Set(SceneView sceneView, bool hitValidTerrain, RaycastHit raycastHit, Texture brushTexture, float brushStrength, float brushSize) { m_SceneView = sceneView; m_BrushTexture = brushTexture; m_BrushStrength = brushStrength; m_BrushSize = brushSize; - m_BrushRotation = brushRotation; + m_HitValidTerrain = hitValidTerrain; + m_RaycastHit = raycastHit; return this; } public SceneView sceneView { get { return m_SceneView; } } public Texture brushTexture { get { return m_BrushTexture; } } public float brushStrength { get { return m_BrushStrength; } } - public float brushRotation { get { return m_BrushRotation; } } public float brushSize { get { return m_BrushSize; } } + public bool hitValidTerrain { get { return m_HitValidTerrain; } } + public RaycastHit raycastHit { get { return m_RaycastHit; } } } internal class OnInspectorGUIContext : IOnInspectorGUI diff --git a/Modules/TerrainEditor/TerrainInspector.cs b/Modules/TerrainEditor/TerrainInspector.cs index fcc226c9cb..b0df34322f 100644 --- a/Modules/TerrainEditor/TerrainInspector.cs +++ b/Modules/TerrainEditor/TerrainInspector.cs @@ -508,6 +508,7 @@ class Styles "console.warnicon"); public GUIContent assign = EditorGUIUtility.TrTextContent("Assign"); + public GUIContent duplicateTab = EditorGUIUtility.TrTextContent("NOTE: Inspector tab is a duplicate. Paint functionality disabled."); // Textures public GUIContent terrainLayers = EditorGUIUtility.TrTextContent("Terrain Layers"); @@ -635,9 +636,9 @@ public bool active static internal ITerrainPaintTool[] m_Tools = null; static internal string[] m_ToolNames = null; - static OnPaintContext onPaintEditContext = new OnPaintContext(null, Vector2.zero, 0.0f, 0.0f, 0.0f); + static OnPaintContext onPaintEditContext = new OnPaintContext(new RaycastHit(), null, Vector2.zero, 0.0f, 0.0f); static OnInspectorGUIContext onInspectorGUIEditContext = new OnInspectorGUIContext(); - static OnSceneGUIContext onSceneGUIEditContext = new OnSceneGUIContext(null, null, 0.0f, 0.0f, 0.0f); + static OnSceneGUIContext onSceneGUIEditContext = new OnSceneGUIContext(null, new RaycastHit(), null, 0.0f, 0.0f); ITerrainPaintTool GetActiveTool() { @@ -813,6 +814,7 @@ void ResetPaintTools() void Initialize() { m_Terrain = target as Terrain; + CheckToolActivation(); } void LoadInspectorSettings() @@ -826,7 +828,7 @@ void LoadInspectorSettings() int selected = EditorPrefs.GetInt("TerrainSelectedBrush", 0); s_DetailPainter.selectedDetail = EditorPrefs.GetInt("TerrainSelectedDetail", 0); - m_ActivePaintToolIndex = EditorPrefs.GetInt("TerraiActivePaintToolIndex", 0); + m_ActivePaintToolIndex = EditorPrefs.GetInt("TerrainActivePaintToolIndex", 0); // TODO: this should be stored by name if (m_ActivePaintToolIndex > m_Tools.Length) m_ActivePaintToolIndex = 0; @@ -844,7 +846,7 @@ void SaveInspectorSettings() EditorPrefs.SetFloat("TerrainBrushSize", m_Size); EditorPrefs.SetFloat("TerrainBrushStrength", m_Strength); - EditorPrefs.SetInt("TerraiActivePaintToolIndex", m_ActivePaintToolIndex); + EditorPrefs.SetInt("TerrainActivePaintToolIndex", m_ActivePaintToolIndex); } public void OnEnable() @@ -874,6 +876,9 @@ public void OnEnable() LoadInspectorSettings(); + // now that tool selection has been loaded from inspector, activate the selected tool + CheckToolActivation(); + InitializeLightingFields(); m_TerrainToolContext = new TerrainToolContext(this); @@ -886,9 +891,11 @@ public void OnEnable() public void OnDisable() { ShortcutIntegration.instance.contextManager.DeregisterToolContext(m_TerrainToolContext); - TerrainPaintUtility.FlushAllPaints(); + PaintContext.ApplyDelayedActions(); SceneView.onSceneGUIDelegate -= OnSceneGUICallback; + SetCurrentPaintToolInactive(); + SaveInspectorSettings(); m_ShowReflectionProbesGUI.valueChanged.RemoveListener(Repaint); @@ -922,6 +929,55 @@ TerrainTool selectedTool Tools.current = Tool.None; m_SelectedTool.value = (int)value; s_activeTerrainInspector = GetInstanceID(); + CheckToolActivation(); + } + } + + // this is a bunch of tracking to ensure we don't mess up the tool mode callbacks + private bool m_PaintToolActive = false; + private void SetCurrentPaintToolActive() + { + if (!m_PaintToolActive) + { + ITerrainPaintTool paintTool = GetActiveTool(); + if (paintTool != null) + { + paintTool.OnEnterToolMode(); + m_PaintToolActive = true; + } + } + } + + private void SetCurrentPaintToolInactive() + { + if (m_PaintToolActive) + { + ITerrainPaintTool paintTool = GetActiveTool(); + if (paintTool != null) + { + paintTool.OnExitToolMode(); + m_PaintToolActive = false; + } + } + } + + // Ideally we would be notified when the active tool changes, but I can see no way to do that + // So instead we will call this function everywhere, which checks for it changing and does the proper notification callbacks + private TerrainTool m_PreviousSelectedTool = TerrainTool.None; + private void CheckToolActivation() + { + TerrainTool currentTool = selectedTool; + if (currentTool != m_PreviousSelectedTool) + { + // inactivate previous tool, if necessary + if (m_PreviousSelectedTool == TerrainTool.Paint) + SetCurrentPaintToolInactive(); + + m_PreviousSelectedTool = currentTool; + + // activate new tool, if necessary + if (currentTool == TerrainTool.Paint) + SetCurrentPaintToolActive(); } } @@ -1390,9 +1446,14 @@ public void ShowPaint() if (m_Tools != null && m_Tools.Length > 1 && m_ToolNames != null) { EditorGUI.BeginChangeCheck(); - m_ActivePaintToolIndex = EditorGUILayout.Popup(m_ActivePaintToolIndex, m_ToolNames); - if (EditorGUI.EndChangeCheck()) + int newPaintToolIndex = EditorGUILayout.Popup(m_ActivePaintToolIndex, m_ToolNames); + if (EditorGUI.EndChangeCheck() && (newPaintToolIndex != m_ActivePaintToolIndex)) + { + SetCurrentPaintToolInactive(); + m_ActivePaintToolIndex = newPaintToolIndex; + SetCurrentPaintToolActive(); Repaint(); + } ITerrainPaintTool activeTool = GetActiveTool(); GUILayout.BeginVertical(EditorStyles.helpBox); @@ -1405,16 +1466,21 @@ public void ShowPaint() public void ShowBrushes(int spacing) { + EditorGUI.BeginDisabledGroup(s_activeTerrainInspector != GetInstanceID() || s_activeTerrainInspectorInstance != this); + GUILayout.Space(spacing); bool repaint = brushList.ShowGUI(); - m_Size = EditorGUILayout.Slider(styles.brushSize, m_Size, 1.0f, Mathf.Min(m_Terrain.terrainData.size.x - 1.0f, m_Terrain.terrainData.size.z - 1.0f)); + float safetyFactorHack = 0.9375f; + m_Size = EditorGUILayout.Slider(styles.brushSize, m_Size, 0.1f, Mathf.Round(Mathf.Min(m_Terrain.terrainData.size.x, m_Terrain.terrainData.size.z) * safetyFactorHack)); m_Strength = PercentSlider(styles.opacity, m_Strength, kMinBrushStrength, 1); // former string formatting: "0.0%" brushList.ShowEditGUI(); if (repaint) Repaint(); + + EditorGUI.EndDisabledGroup(); } void ResizeControlTexture(int newResolution) @@ -1694,6 +1760,12 @@ public override void OnInspectorGUI() return; } + if (s_activeTerrainInspector != GetInstanceID() || s_activeTerrainInspectorInstance != this) + { + GUILayout.BeginVertical(EditorStyles.helpBox); + GUILayout.Label(styles.duplicateTab, EditorStyles.boldLabel); + GUILayout.EndVertical(); + } if (Event.current.type == EventType.Layout) m_TerrainCollider = m_Terrain.gameObject.GetComponent(); @@ -1712,6 +1784,8 @@ public override void OnInspectorGUI() GUILayout.EndVertical(); } + EditorGUI.BeginDisabledGroup(s_activeTerrainInspector != GetInstanceID() || s_activeTerrainInspectorInstance != this); + // Show the master tool selector GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); // flexible space on either end centers the toolbar @@ -1750,6 +1824,8 @@ public override void OnInspectorGUI() GUILayout.EndVertical(); } + EditorGUI.EndDisabledGroup(); + switch ((TerrainTool)tool) { case TerrainTool.Paint: @@ -1866,23 +1942,21 @@ private bool IsModificationToolActive() return true; } - bool IsBrushPreviewVisible() + bool IsBrushPreviewVisible(Terrain overTerrain) { if (!IsModificationToolActive()) return false; - Vector3 pos; - Vector2 uv; - return Raycast(out uv, out pos); + return (overTerrain != null); } - private bool RaycastAllTerrains(out Terrain hitTerrain, out Vector2 hitUV) + private bool RaycastAllTerrains(out Terrain hitTerrain, out RaycastHit raycastHit) { Ray mouseRay = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); float minDist = float.MaxValue; hitTerrain = null; - hitUV = Vector2.zero; + raycastHit = new RaycastHit(); foreach (Terrain terrain in Terrain.activeTerrains) { RaycastHit hit; @@ -1892,11 +1966,11 @@ private bool RaycastAllTerrains(out Terrain hitTerrain, out Vector2 hitUV) { minDist = hit.distance; hitTerrain = terrain; - hitUV = hit.textureCoord; + raycastHit = hit; } } } - return hitTerrain; + return (hitTerrain != null); } public void OnSceneGUICallback(SceneView sceneView) @@ -1905,47 +1979,56 @@ public void OnSceneGUICallback(SceneView sceneView) Event e = Event.current; - Terrain terrain = null; - Vector2 uv = Vector2.zero; + Terrain hitTerrain = null; + RaycastHit raycastHit = new RaycastHit(); + + // If this is not the active terrain inspector, we shouldn't be affecting the SceneGUI + if (s_activeTerrainInspector != GetInstanceID() || s_activeTerrainInspectorInstance != this) + return; if (selectedTool == TerrainTool.Paint || selectedTool == TerrainTool.PaintDetail || selectedTool == TerrainTool.PlaceTree) { - if (RaycastAllTerrains(out terrain, out uv)) + if (RaycastAllTerrains(out hitTerrain, out raycastHit)) { if (e.type == EventType.MouseDown || e.type == EventType.MouseUp) { if (e.button == 0 && !Event.current.alt) - Selection.activeObject = terrain; + Selection.activeObject = hitTerrain; } } } + Vector2 uv = raycastHit.textureCoord; - bool isTerrainValid = (terrain != null && terrain.terrainData != null); + bool hitValidTerrain = (hitTerrain != null && hitTerrain.terrainData != null); + if (!hitValidTerrain) + { + raycastHit = new RaycastHit(); + } if (selectedTool == TerrainTool.Paint) { - Terrain lastActiveTerrain = isTerrainValid ? terrain : s_LastActiveTerrain; + Terrain lastActiveTerrain = hitValidTerrain ? hitTerrain : s_LastActiveTerrain; if (lastActiveTerrain) { ITerrainPaintTool activeTool = GetActiveTool(); - activeTool.OnSceneGUI(lastActiveTerrain, onSceneGUIEditContext.Set(sceneView, brushList.GetActiveBrush().texture, m_Strength, 0.0f, m_Size)); + activeTool.OnSceneGUI(lastActiveTerrain, onSceneGUIEditContext.Set(sceneView, hitValidTerrain, raycastHit, brushList.GetActiveBrush().texture, m_Strength, m_Size)); } } else if (selectedTool == TerrainTool.PaintDetail || selectedTool == TerrainTool.PlaceTree) { - if (isTerrainValid) + if (hitValidTerrain) { float brushSize = selectedTool == TerrainTool.PlaceTree ? TreePainter.brushSize : m_Size; - TerrainPaintUtilityEditor.ShowDefaultPreviewBrush(terrain, brushList.GetActiveBrush().texture, m_Strength, brushSize, 0.0f); + TerrainPaintUtilityEditor.ShowDefaultPreviewBrush(hitTerrain, brushList.GetCircleBrush().texture, brushSize); } } - if (!isTerrainValid) + if (!hitValidTerrain) return; - s_LastActiveTerrain = terrain; + s_LastActiveTerrain = hitTerrain; int id = GUIUtility.GetControlID(s_TerrainEditorHash, FocusType.Passive); switch (e.GetTypeForControl(id)) @@ -1957,7 +2040,7 @@ public void OnSceneGUICallback(SceneView sceneView) break; case EventType.MouseMove: - if (IsBrushPreviewVisible()) + if (IsBrushPreviewVisible(hitTerrain)) HandleUtility.Repaint(); break; @@ -2000,12 +2083,12 @@ public void OnSceneGUICallback(SceneView sceneView) TreePainter.BeginPlaceTrees(m_Terrain); } - TreePainter.PlaceTrees(terrain, uv.x, uv.y); + TreePainter.PlaceTrees(hitTerrain, uv.x, uv.y); } } else { - TreePainter.RemoveTrees(terrain, uv.x, uv.y, Event.current.control); + TreePainter.RemoveTrees(hitTerrain, uv.x, uv.y, Event.current.control); } } else if (selectedTool == TerrainTool.PaintDetail) @@ -2016,13 +2099,13 @@ public void OnSceneGUICallback(SceneView sceneView) } DetailPaintOperation paintOp = new DetailPaintOperation(); - paintOp.size = (int)Mathf.Max(1.0f, ((float)m_Size * ((float)terrain.terrainData.detailResolution / terrain.terrainData.size.x))); + paintOp.size = (int)Mathf.Max(1.0f, ((float)m_Size * ((float)hitTerrain.terrainData.detailResolution / hitTerrain.terrainData.size.x))); paintOp.targetStrength = m_DetailStrength * 16F; if (Event.current.shift || Event.current.control) paintOp.targetStrength *= -1; paintOp.opacity = m_DetailOpacity; paintOp.clearSelectedOnly = Event.current.control; - paintOp.terrainData = terrain.terrainData; + paintOp.terrainData = hitTerrain.terrainData; paintOp.brush = brushList.GetActiveBrush(); paintOp.tool = selectedTool; paintOp.randomizeDetails = true; @@ -2034,10 +2117,10 @@ public void OnSceneGUICallback(SceneView sceneView) else { ITerrainPaintTool activeTool = GetActiveTool(); - if (activeTool.OnPaint(terrain, onPaintEditContext.Set(brushList.GetActiveBrush().texture, uv, m_Strength, 0.0f, m_Size))) + if (activeTool.OnPaint(hitTerrain, onPaintEditContext.Set(hitValidTerrain, raycastHit, brushList.GetActiveBrush().texture, uv, m_Strength, m_Size))) { // height map modification modes - terrain.editorRenderFlags = TerrainRenderFlags.Heightmap; + hitTerrain.editorRenderFlags = TerrainRenderFlags.Heightmap; } } @@ -2067,8 +2150,8 @@ public void OnSceneGUICallback(SceneView sceneView) s_DetailPainter.EndPaintDetails(); } - terrain.editorRenderFlags = TerrainRenderFlags.All; - TerrainPaintUtility.FlushAllPaints(); + hitTerrain.editorRenderFlags = TerrainRenderFlags.All; + PaintContext.ApplyDelayedActions(); e.Use(); } diff --git a/Modules/TerrainEditor/Utilities/TerrainPaintUtilityEditor.cs b/Modules/TerrainEditor/Utilities/TerrainPaintUtilityEditor.cs index 39ccf3c7f7..b054015c93 100644 --- a/Modules/TerrainEditor/Utilities/TerrainPaintUtilityEditor.cs +++ b/Modules/TerrainEditor/Utilities/TerrainPaintUtilityEditor.cs @@ -14,12 +14,6 @@ namespace UnityEditor.Experimental.TerrainAPI { public static class TerrainPaintUtilityEditor { - internal enum BrushPreviewMeshType - { - QuadOutline = 0, - QuadPatch - } - // This maintains the list of terrains we have touched in the current operation (and the current operation identifier, as an undo group) // We track this to have good cross-tile undo support: each modified tile should be added, at most, ONCE within a single operation private static int s_CurrentOperationUndoGroup = -1; @@ -27,7 +21,7 @@ internal enum BrushPreviewMeshType static TerrainPaintUtilityEditor() { - TerrainPaintUtility.onTerrainTileBeforePaint += (tile, action, editorUndoName) => + PaintContext.onTerrainTileBeforePaint += (tile, action, editorUndoName) => { // if we are in a new undo group (new operation) then start with an empty list if (Undo.GetCurrentGroup() != s_CurrentOperationUndoGroup) @@ -36,7 +30,7 @@ static TerrainPaintUtilityEditor() s_CurrentOperationUndoStack.Clear(); } - if (tile == null || string.IsNullOrEmpty(editorUndoName) || tile.rect.width == 0 || tile.rect.height == 0) + if (tile == null || string.IsNullOrEmpty(editorUndoName)) return; if (!s_CurrentOperationUndoStack.Contains(tile.terrain)) @@ -44,7 +38,7 @@ static TerrainPaintUtilityEditor() s_CurrentOperationUndoStack.Add(tile.terrain); var undoObjects = new List(); undoObjects.Add(tile.terrain.terrainData); - if (0 != (action & TerrainPaintUtility.ToolAction.PaintTexture)) + if (0 != (action & PaintContext.ToolAction.PaintTexture)) undoObjects.AddRange(tile.terrain.terrainData.alphamapTextures); Undo.RegisterCompleteObjectUndo(undoObjects.ToArray(), editorUndoName); } @@ -67,31 +61,15 @@ internal static void UpdateTerrainDataUndo(TerrainData terrainData, string undoN } } - public static void ShowDefaultPreviewBrush(Terrain terrain, Texture brushTexture, float brushStrength, float brushSize, float futurePreviewScale) + public static void ShowDefaultPreviewBrush(Terrain terrain, Texture brushTexture, float brushSize) { Ray mouseRay = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); RaycastHit hit; if (terrain.GetComponent().Raycast(mouseRay, out hit, Mathf.Infinity)) { - if (Event.current.shift) - brushStrength = -brushStrength; - - Rect brushRect = TerrainPaintUtility.CalculateBrushRectInTerrainUnits(terrain, hit.textureCoord, brushSize); - TerrainPaintUtility.PaintContext ctx = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushRect); - - ctx.sourceRenderTexture.filterMode = FilterMode.Bilinear; - brushTexture.filterMode = FilterMode.Bilinear; - - Vector2 topLeft = ctx.brushRect.min; - float xfrac = ((topLeft.x - (int)topLeft.x) / (float)ctx.sourceRenderTexture.width); - float yfrac = ((topLeft.y - (int)topLeft.y) / (float)ctx.sourceRenderTexture.height); - - Vector4 texScaleOffset = new Vector4(0.5f, 0.5f, 0.5f + xfrac + 0.5f / (float)ctx.sourceRenderTexture.width, 0.5f + yfrac + 0.5f / (float)ctx.sourceRenderTexture.height); - - DrawDefaultBrushPreviewMesh(terrain, hit, ctx.sourceRenderTexture, brushTexture, brushStrength * 0.01f, brushSize, defaultPreviewPatchMesh, false, texScaleOffset); - if ((futurePreviewScale > Mathf.Epsilon) && Event.current.control) - DrawDefaultBrushPreviewMesh(terrain, hit, ctx.sourceRenderTexture, brushTexture, futurePreviewScale, brushSize, defaultPreviewPatchMesh, true, texScaleOffset); - + BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, hit.textureCoord, brushSize, 0.0f); + PaintContext ctx = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds(), 1); + DrawBrushPreview(ctx, TerrainPaintUtilityEditor.BrushPreview.SourceRenderTexture, brushTexture, brushXform, GetDefaultBrushPreviewMaterial(), 0); TerrainPaintUtility.ReleaseContextResources(ctx); } } @@ -103,83 +81,96 @@ public static Material GetDefaultBrushPreviewMaterial() return m_BrushPreviewMaterial; } - // drawing utilities - - internal static Mesh GenerateDefaultBrushPreviewMesh(int tesselationLevel) + public enum BrushPreview { - if (tesselationLevel < 1) - { - Debug.LogWarning("Invalid tessellation level passed to GenerateBrushPreviewMesh."); - return null; - } - - var verts = new List(); - var indices = new List(); - - Mesh m = new Mesh(); - - float incr = 2.0f / (float)tesselationLevel; - - Vector3 v = new Vector3(-1.0f, 0, -1.0f); - - for (int i = 0; i <= tesselationLevel; i++) - { - v.x = -1.0f; - for (int j = 0; j <= tesselationLevel; j++) - { - verts.Add(v); - v.x += incr; - - if (i != tesselationLevel && j != tesselationLevel) - { - int index = i * (tesselationLevel + 1) + j; - - indices.Add(index); - indices.Add(index + tesselationLevel + 1); - indices.Add(index + 1); - - indices.Add(index + 1); - indices.Add(index + tesselationLevel + 1); - indices.Add(index + tesselationLevel + 2); - } - } - v.z += incr; - } - - m.vertices = verts.ToArray(); - m.triangles = indices.ToArray(); - - return m; - } - - public static void DrawDefaultBrushPreviewMesh(Terrain terrain, RaycastHit hit, Texture heightmapTexture, Texture brushTexture, float brushStrength, float brushSize, Mesh mesh, bool showPreviewPostBrush, Vector4 texScaleOffset) + SourceRenderTexture, + DestinationRenderTexture + }; + + public static void DrawBrushPreview( + PaintContext heightmapPC, + BrushPreview previewTexture, + Texture brushTexture, // brush texture to apply + BrushTransform brushXform, // brush transform that defines the brush UV space + Material proceduralMaterial, // the material to render with (must support procedural quad-mesh generation) + int materialPassIndex) // the pass to use within the material { - Vector4 brushParams = new Vector4(brushStrength, 2.0f * terrain.terrainData.heightmapScale.y, 0.0f, 0.0f); - - Material mat = GetDefaultBrushPreviewMaterial(); - mat.SetTexture("_MainTex", heightmapTexture); - mat.SetTexture("_BrushTex", brushTexture); - mat.SetVector("_BrushParams", brushParams); - mat.SetVector("_TexScaleOffet", texScaleOffset); - mat.SetPass(showPreviewPostBrush ? 1 : 0); - - Matrix4x4 matrix = Matrix4x4.identity; - matrix.SetTRS(new Vector3(hit.point.x, terrain.GetPosition().y, hit.point.z), Quaternion.identity, new Vector3(brushSize / 2.0f, 1, brushSize / 2.0f)); - - Graphics.DrawMeshNow(mesh, matrix); + // we want to build a quad mesh, with one vertex for each pixel in the heightmap + // i.e. a 3x3 heightmap would create a mesh that looks like this: + // + // +-+-+ + // |\|\| + // +-+-+ + // |\|\| + // +-+-+ + // + int quadsX = heightmapPC.pixelRect.width - 1; + int quadsY = heightmapPC.pixelRect.height - 1; + int vertexCount = quadsX * quadsY * (2 * 3); // two triangles (2 * 3 vertices) per quad + + // this is used to tessellate the quad mesh (from within the vertex shader) + proceduralMaterial.SetVector("_QuadRez", new Vector4(quadsX, quadsY, vertexCount, 0.0f)); + + // paint context pixels to heightmap uv: uv = (pixels + 0.5) / width + Texture heightmapTexture = (previewTexture == BrushPreview.SourceRenderTexture) ? heightmapPC.sourceRenderTexture : heightmapPC.destinationRenderTexture; + float invWidth = 1.0f / heightmapTexture.width; + float invHeight = 1.0f / heightmapTexture.height; + proceduralMaterial.SetVector("_HeightmapUV_PCPixelsX", new Vector4(invWidth, 0.0f, 0.0f, 0.0f)); + proceduralMaterial.SetVector("_HeightmapUV_PCPixelsY", new Vector4(0.0f, invHeight, 0.0f, 0.0f)); + proceduralMaterial.SetVector("_HeightmapUV_Offset", new Vector4(0.5f * invWidth, 0.5f * invHeight, 0.0f, 0.0f)); + + // make sure we point filter the heightmap + FilterMode oldFilter = heightmapTexture.filterMode; + heightmapTexture.filterMode = FilterMode.Point; + proceduralMaterial.SetTexture("_Heightmap", heightmapTexture); + + // paint context pixels to object (terrain) position + // objectPos.x = scaleX * pcPixels.x + heightmapRect.xMin * scaleX + // objectPos.y = scaleY * H + // objectPos.z = scaleZ * pcPixels.y + heightmapRect.yMin * scaleZ + float scaleX = heightmapPC.pixelSize.x; + float scaleY = 2.0f * heightmapPC.originTerrain.terrainData.heightmapScale.y; + float scaleZ = heightmapPC.pixelSize.y; + proceduralMaterial.SetVector("_ObjectPos_PCPixelsX", new Vector4(scaleX, 0.0f, 0.0f, 0.0f)); + proceduralMaterial.SetVector("_ObjectPos_HeightMapSample", new Vector4(0.0f, scaleY, 0.0f, 0.0f)); + proceduralMaterial.SetVector("_ObjectPos_PCPixelsY", new Vector4(0.0f, 0.0f, scaleZ, 0.0f)); + proceduralMaterial.SetVector("_ObjectPos_Offset", new Vector4(heightmapPC.pixelRect.xMin * scaleX, 0.0f, heightmapPC.pixelRect.yMin * scaleZ, 1.0f)); + + // heightmap paint context pixels to brush UV + // derivation: + + // BrushUV = f(terrainSpace.xz) = f(g(pcPixels.xy)) + // f(ts.xy) = ts.x * brushXform.X + ts.y * brushXform.Y + brushXform.Origin + // g(pcPixels.xy) = ts.xz = pcOrigin + pcPixels.xy * pcSize + // f(g(pcPixels.uv)) == (pcOrigin + pcPixels.uv * pcSize).x * brushXform.X + (pcOrigin + pcPixels.uv * pcSize).y * brushXform.Y + brushXform.Origin + // f(g(pcPixels.uv)) == (pcOrigin.x + pcPixels.u * pcSize.x) * brushXform.X + (pcOrigin.y + pcPixels.v * pcSize.y) * brushXform.Y + brushXform.Origin + // f(g(pcPixels.uv)) == (pcOrigin.x * brushXform.X) + (pcPixels.u * pcSize.x) * brushXform.X + (pcOrigin.y * brushXform.Y) + (pcPixels.v * pcSize.y) * brushXform.Y + brushXform.Origin + // f(g(pcPixels.uv)) == pcPixels.u * (pcSize.x * brushXform.X) + pcPixels.v * (pcSize.y * brushXform.Y) + (brushXform.Origin + (pcOrigin.x * brushXform.X) + (pcOrigin.y * brushXform.Y)) + + // paint context origin in terrain space + // (note this is the UV space origin and size, not the mesh origin & size) + float pcOriginX = heightmapPC.pixelRect.xMin * heightmapPC.pixelSize.x; + float pcOriginZ = heightmapPC.pixelRect.yMin * heightmapPC.pixelSize.y; + float pcSizeX = heightmapPC.pixelSize.x; + float pcSizeZ = heightmapPC.pixelSize.y; + + Vector2 scaleU = pcSizeX * brushXform.targetX; + Vector2 scaleV = pcSizeZ * brushXform.targetY; + Vector2 offset = brushXform.targetOrigin + pcOriginX * brushXform.targetX + pcOriginZ * brushXform.targetY; + proceduralMaterial.SetVector("_BrushUV_PCPixelsX", new Vector4(scaleU.x, scaleU.y, 0.0f, 0.0f)); + proceduralMaterial.SetVector("_BrushUV_PCPixelsY", new Vector4(scaleV.x, scaleV.y, 0.0f, 0.0f)); + proceduralMaterial.SetVector("_BrushUV_Offset", new Vector4(offset.x, offset.y, 0.0f, 1.0f)); + proceduralMaterial.SetTexture("_BrushTex", brushTexture); + + Vector3 terrainPos = heightmapPC.originTerrain.GetPosition(); + proceduralMaterial.SetVector("_TerrainObjectToWorldOffset", terrainPos); + + proceduralMaterial.SetPass(materialPassIndex); + Graphics.DrawProcedural(MeshTopology.Triangles, vertexCount); + + heightmapTexture.filterMode = oldFilter; } - static Mesh m_DefaultPreviewPatchMesh = null; static Material m_BrushPreviewMaterial = null; - - public static Mesh defaultPreviewPatchMesh - { - get - { - if (m_DefaultPreviewPatchMesh == null) - m_DefaultPreviewPatchMesh = GenerateDefaultBrushPreviewMesh(100); - return m_DefaultPreviewPatchMesh; - } - } } } diff --git a/Modules/TextCore/Managed/AssemblyInfo.cs b/Modules/TextCore/Managed/AssemblyInfo.cs index 6e1c470b4e..8400fd720c 100644 --- a/Modules/TextCore/Managed/AssemblyInfo.cs +++ b/Modules/TextCore/Managed/AssemblyInfo.cs @@ -9,3 +9,5 @@ [assembly: InternalsVisibleTo("Unity.TextCore.Editor")] [assembly: InternalsVisibleTo("Unity.TextMeshPro")] [assembly: InternalsVisibleTo("Unity.TextMeshPro.Editor")] +[assembly: InternalsVisibleTo("Unity.FontEngine.Tests")] +[assembly: InternalsVisibleTo("Unity.FontEngine.Editor.Tests")] diff --git a/Modules/TextCore/Managed/FaceInfo.cs b/Modules/TextCore/Managed/FaceInfo.cs new file mode 100644 index 0000000000..fe1b8fadb9 --- /dev/null +++ b/Modules/TextCore/Managed/FaceInfo.cs @@ -0,0 +1,261 @@ +// 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.Runtime.InteropServices; +using UnityEngine.Bindings; +using UnityEngine.Scripting; +using UnityEngine.TextCore.LowLevel; + + +namespace UnityEngine.TextCore +{ + /// + /// A structure that contains information about a given typeface and for a specific point size. + /// + [Serializable] + [UsedByNativeCode] + [StructLayout(LayoutKind.Sequential)] + public struct FaceInfo + { + /// + /// The name of the font typeface also known as family name. + /// + public string familyName { get { return m_FamilyName; } set { m_FamilyName = value; } } + + /// + /// The style name of the typeface which defines both the visual style and weight of the typeface. + /// + public string styleName { get { return m_StyleName; } set { m_StyleName = value; } } + + /// + /// The point size used for sampling the typeface. This is also referenced as sampling point size. + /// + public int pointSize { get { return m_PointSize; } set { m_PointSize = value; } } + + /// + /// The relative scale of the typeface. + /// Default value is 1.0f. + /// + public float scale { get { return m_Scale; } set { m_Scale = value; } } + + // Key metrics for the typeface + + /// + /// The line height represents the distance between consecutive lines of text. + /// This is the distance from baseline to baseline. It is usually computed as line height = ascent - descent + line gap. + /// + public float lineHeight { get { return m_LineHeight; } set { m_LineHeight = value; } } + + /// + /// The Ascent line is typically located at the top of the tallest glyph in the typeface. This represents the distance between the baseline and the tallest ascender. This value is usually positive. + /// + public float ascentLine { get { return m_AscentLine; } set { m_AscentLine = value; } } + + /// + /// The Cap line is typically located at the top of capital letters. This value represents the distance between the baseline and the top of capital letters. + /// + public float capLine { get { return m_CapLine; } set { m_CapLine = value; } } + + /// + /// The Mean line is typically located at the top of lowercase letters. This value represents the distance between the baseline and the top of lowercase letters. + /// + public float meanLine { get { return m_MeanLine; } set { m_MeanLine = value; } } + + /// + /// The Baseline is an imaginary line upon which all glyphs appear to rest on. This value is typically zero. + /// + public float baseline { get { return m_Baseline; } set { m_Baseline = value; } } + + /// + /// The Descent line is typically located at the bottom of the glyph with the lowest descender in the typeface. This represents the distance between the baseline and the lowest descender. This value is usually negative. + /// + public float descentLine { get { return m_DescentLine; } set { m_DescentLine = value; } } + + /// + /// The position of characters using superscript. + /// + public float superscriptOffset { get { return m_SuperscriptOffset; } set { m_SuperscriptOffset = value; } } + + /// + /// The relative size / scale of superscript characters. + /// + public float superscriptSize { get { return m_SuperscriptSize; } set { m_SuperscriptSize = value; } } + + /// + /// The position of characters using subscript. + /// + public float subscriptOffset { get { return m_SubscriptOffset; } set { m_SubscriptOffset = value; } } + + /// + /// The relative size / scale of subscript characters. + /// + public float subscriptSize { get { return m_SubscriptSize; } set { m_SubscriptSize = value; } } + + /// + /// The position of the underline. + /// + public float underlineOffset { get { return m_UnderlineOffset; } set { m_UnderlineOffset = value; } } + + /// + /// The thickness of the underline. + /// + public float underlineThickness { get { return m_UnderlineThickness; } set { m_UnderlineThickness = value; } } + + /// + /// The position of the strikethrough. + /// + public float strikethroughOffset { get { return m_StrikethroughOffset; } set { m_StrikethroughOffset = value; } } + + /// + /// The thickness of the strikethrough. + /// + public float strikethroughThickness { get { return m_StrikethroughThickness; } set { m_StrikethroughThickness = value; } } + + /// + /// The width of the tab character. This width is typically the same as the space character. + /// + public float tabWidth { get { return m_TabWidth; } set { m_TabWidth = value; } } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + [NativeName("familyName")] + private string m_FamilyName; + + [SerializeField] + [NativeName("styleName")] + private string m_StyleName; + + [SerializeField] + [NativeName("pointSize")] + private int m_PointSize; + + [SerializeField] + [NativeName("scale")] + private float m_Scale; + + [SerializeField] + [NativeName("lineHeight")] + private float m_LineHeight; + + [SerializeField] + [NativeName("ascentLine")] + private float m_AscentLine; + + [SerializeField] + [NativeName("capLine")] + private float m_CapLine; + + [SerializeField] + [NativeName("meanLine")] + private float m_MeanLine; + + [SerializeField] + [NativeName("baseline")] + private float m_Baseline; + + [SerializeField] + [NativeName("descentLine")] + private float m_DescentLine; + + [SerializeField] + [NativeName("superscriptOffset")] + private float m_SuperscriptOffset; + + [SerializeField] + [NativeName("superscriptSize")] + private float m_SuperscriptSize; + + [SerializeField] + [NativeName("subscriptOffset")] + private float m_SubscriptOffset; + + [SerializeField] + [NativeName("subscriptSize")] + private float m_SubscriptSize; + + [SerializeField] + [NativeName("underlineOffset")] + private float m_UnderlineOffset; + + [SerializeField] + [NativeName("underlineThickness")] + private float m_UnderlineThickness; + + [SerializeField] + [NativeName("strikethroughOffset")] + private float m_StrikethroughOffset; + + [SerializeField] + [NativeName("strikethroughThickness")] + private float m_StrikethroughThickness; + + [SerializeField] + [NativeName("tabWidth")] + private float m_TabWidth; + + /// + /// Constructor used for testing + /// + internal FaceInfo(string familyName, string styleName, int pointSize, float scale, float lineHeight, float ascentLine, float capLine, float meanLine, float baseline, float descentLine, float superscriptOffset, float superscriptSize, float subscriptOffset, float subscriptSize, float underlineOffset, float underlineThickness, float strikethroughOffset, float strikethroughThickness, float tabWidth) + { + m_FamilyName = familyName; + m_StyleName = styleName; + + m_PointSize = pointSize; + m_Scale = scale; + + m_LineHeight = lineHeight; + m_AscentLine = ascentLine; + m_CapLine = capLine; + m_MeanLine = meanLine; + m_Baseline = baseline; + m_DescentLine = descentLine; + + m_SuperscriptOffset = superscriptOffset; + m_SuperscriptSize = superscriptSize; + m_SubscriptOffset = subscriptOffset; + m_SubscriptSize = subscriptSize; + + m_UnderlineOffset = underlineOffset; + m_UnderlineThickness = underlineThickness; + + m_StrikethroughOffset = strikethroughOffset; + m_StrikethroughThickness = strikethroughThickness; + + m_TabWidth = tabWidth; + } + + /// + /// Compares the information in this FaceInfo structure with the information in the given FaceInfo structure to determine whether they have the same values. + /// + /// The FaceInfo structure to compare this FaceInfo structure with. + /// Returns true if the FaceInfo structures have the same values. False if not. + public bool Compare(FaceInfo other) + { + return familyName == other.familyName && + styleName == other.styleName && + pointSize == other.pointSize && + FontEngineUtilities.Approximately(scale, other.scale) && + FontEngineUtilities.Approximately(lineHeight, other.lineHeight) && + FontEngineUtilities.Approximately(ascentLine, other.ascentLine) && + FontEngineUtilities.Approximately(capLine, other.capLine) && + FontEngineUtilities.Approximately(meanLine, other.meanLine) && + FontEngineUtilities.Approximately(baseline, other.baseline) && + FontEngineUtilities.Approximately(descentLine, other.descentLine) && + FontEngineUtilities.Approximately(superscriptOffset, other.superscriptOffset) && + FontEngineUtilities.Approximately(superscriptSize, other.superscriptSize) && + FontEngineUtilities.Approximately(subscriptOffset, other.subscriptOffset) && + FontEngineUtilities.Approximately(subscriptSize, other.subscriptSize) && + FontEngineUtilities.Approximately(underlineOffset, other.underlineOffset) && + FontEngineUtilities.Approximately(underlineThickness, other.underlineThickness) && + FontEngineUtilities.Approximately(strikethroughOffset, other.strikethroughOffset) && + FontEngineUtilities.Approximately(strikethroughThickness, other.strikethroughThickness) && + FontEngineUtilities.Approximately(tabWidth, other.tabWidth); + } + } +} diff --git a/Modules/TextCore/Managed/Glyph.cs b/Modules/TextCore/Managed/Glyph.cs index 0499ff254c..0aa3275e2d 100644 --- a/Modules/TextCore/Managed/Glyph.cs +++ b/Modules/TextCore/Managed/Glyph.cs @@ -6,211 +6,367 @@ using System.Runtime.InteropServices; using UnityEngine.Bindings; using UnityEngine.Scripting; +using UnityEngine.TextCore.LowLevel; namespace UnityEngine.TextCore { /// - /// A Glyph is the visual representation of a text element / character. + /// A rectangle that defines the position of a glyph within an atlas texture. /// [Serializable] - [NativeAsStruct] [UsedByNativeCode] [StructLayout(LayoutKind.Sequential)] - public class Glyph + public struct GlyphRect : IEquatable { /// - /// Constructor for a new glyph. + /// The x position of the glyph in the font atlas texture. /// - public Glyph() - { - this.index = 0; - this.x = 0; - this.y = 0; - this.width = 0; - this.height = 0; - this.bearingX = 0; - this.bearingY = 0; - this.advanceX = 0; - this.scale = 1; - this.atlasIndex = 0; - } + public int x { get { return m_X; } set { m_X = value; } } /// - /// Constructor for a new glyph + /// The y position of the glyph in the font atlas texture. /// - /// Glyph whose values are copied to the new glyph. - public Glyph(Glyph glyph) - { - this.index = glyph.index; - this.x = glyph.x; - this.y = glyph.y; - this.width = glyph.width; - this.height = glyph.height; - this.bearingX = glyph.bearingX; - this.bearingY = glyph.bearingY; - this.advanceX = glyph.advanceX; - this.scale = glyph.scale; - this.atlasIndex = glyph.atlasIndex; - } + public int y { get { return m_Y; } set { m_Y = value; } } /// - /// Constructor for a new glyph + /// The width of the glyph. /// - /// Index of the glyph. - /// The bearingX of the glyph. - /// The bearingY of the glyph. - /// The width of the glyph. - /// The height of the glyph. - /// The advanceX of the glyph. - /// The relative scale of the glyph. - public Glyph(uint index, int bearingX, int bearingY, int width, int height, int advanceX, float scale, int atlasIndex) - { - this.index = index; - this.x = 0; - this.y = 0; - this.width = width; - this.height = height; - this.bearingX = bearingX; - this.bearingY = bearingY; - this.advanceX = advanceX; - this.scale = scale; - this.atlasIndex = atlasIndex; - } + public int width { get { return m_Width; } set { m_Width = value; } } /// - /// The index of the glyph in the source font file. + /// The height of the glyph. /// - public uint index; + public int height { get { return m_Height; } set { m_Height = value; } } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + [NativeName("x")] + private int m_X; + + [SerializeField] + [NativeName("y")] + private int m_Y; + + [SerializeField] + [NativeName("width")] + private int m_Width; + + [SerializeField] + [NativeName("height")] + private int m_Height; + + static readonly GlyphRect s_ZeroGlyphRect = new GlyphRect(0, 0, 0, 0); /// - /// The point size at which this glyph was rastered. + /// A GlyphRect with all values set to zero. Shorthand for writing GlyphRect(0, 0, 0, 0). /// - //public int pointSize; + public static GlyphRect zero { get { return s_ZeroGlyphRect; } } /// - /// The x position of the glyph in the font atlas texture + /// Constructor for new GlyphRect. /// - public int x { get { return m_XMin; } set { m_XMin = value; } } - [SerializeField] - [NativeName("x")] - private int m_XMin; + /// The x position of the glyph in the atlas texture. + /// The y position of the glyph in the atlas texture. + /// The width of the glyph. + /// The height of the glyph. + public GlyphRect(int x, int y, int width, int height) + { + m_X = x; + m_Y = y; + m_Width = width; + m_Height = height; + } /// - /// The y position of the glyph in the font atlas texture. + /// Construct new GlyphRect from a Rect. /// - public int y { get { return m_YMin; } set { m_YMin = value; } } - [SerializeField] - [NativeName("y")] - private int m_YMin; + /// The Rect used to construct the new GlyphRect. + public GlyphRect(Rect rect) + { + m_X = (int)rect.x; + m_Y = (int)rect.y; + m_Width = (int)rect.width; + m_Height = (int)rect.height; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + public bool Equals(GlyphRect other) + { + return base.Equals(other); + } + public static bool operator==(GlyphRect lhs, GlyphRect rhs) + { + return lhs.x == rhs.x && + lhs.y == rhs.y && + lhs.width == rhs.width && + lhs.height == rhs.height; + } - // ======================= - // Glyph Metrics - // ======================= + public static bool operator!=(GlyphRect lhs, GlyphRect rhs) + { + return !(lhs == rhs); + } + } + /// + /// A set of values that define the size, position and spacing of a glyph when performing text layout. + /// + [Serializable] + [UsedByNativeCode] + [StructLayout(LayoutKind.Sequential)] + public struct GlyphMetrics : IEquatable + { /// /// The width of the glyph. /// - public int width { get { return m_Width; } set { m_Width = value; } } - [SerializeField] - [NativeName("width")] - private int m_Width; + public float width { get { return m_Width; } set { m_Width = value; } } /// /// The height of the glyph. /// - public int height { get { return m_Height; } set { m_Height = value; } } - [SerializeField] - [NativeName("height")] - private int m_Height; + public float height { get { return m_Height; } set { m_Height = value; } } /// - /// The horizontal distance from the current drawing position (origin) relative to the elements' left bounding box edge (bbox). + /// The horizontal distance from the current drawing position (origin) relative to the element's left bounding box edge (bbox). /// - public int bearingX; + public float horizontalBearingX { get { return m_HorizontalBearingX; } set { m_HorizontalBearingX = value; } } /// - /// The vertical distance from the current baseline relative to the elements' top bounding box edge (bbox). + /// The vertical distance from the current baseline relative to the element's top bounding box edge (bbox). /// - public int bearingY; + public float horizontalBearingY { get { return m_HorizontalBearingY; } set { m_HorizontalBearingY = value; } } /// /// The horizontal distance to increase (left to right) or decrease (right to left) the drawing position relative to the origin of the text element. - /// This determines the origin position of the next element. + /// This determines the origin position of the next text element. /// - public int advanceX; + public float horizontalAdvance { get { return m_HorizontalAdvance; } set { m_HorizontalAdvance = value; } } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + [NativeName("width")] + private float m_Width; + + [SerializeField] + [NativeName("height")] + private float m_Height; + + [SerializeField] + [NativeName("horizontalBearingX")] + private float m_HorizontalBearingX; + + [SerializeField] + [NativeName("horizontalBearingy")] + private float m_HorizontalBearingY; + + [SerializeField] + [NativeName("horizontalAdvance")] + private float m_HorizontalAdvance; /// - /// The relative scale of the text element. The default value is 1.0. + /// Constructor for new glyph metrics. /// - public float scale; + /// The width of the glyph. + /// The height of the glyph. + /// The horizontal bearingX. + /// The horizontal bearingY. + /// The horizontal advance. + public GlyphMetrics(float width, float height, float bearingX, float bearingY, float advance) + { + m_Width = width; + m_Height = height; + m_HorizontalBearingX = bearingX; + m_HorizontalBearingY = bearingY; + m_HorizontalAdvance = advance; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + public bool Equals(GlyphMetrics other) + { + return base.Equals(other); + } + public static bool operator==(GlyphMetrics lhs, GlyphMetrics rhs) + { + return lhs.width == rhs.width && + lhs.height == rhs.height && + lhs.horizontalBearingX == rhs.horizontalBearingX && + lhs.horizontalBearingY == rhs.horizontalBearingY && + lhs.horizontalAdvance == rhs.horizontalAdvance; + } - // ======================= - // Font Atlas Information - // ======================= + public static bool operator!=(GlyphMetrics lhs, GlyphMetrics rhs) + { + return !(lhs == rhs); + } + } + /// + /// A Glyph is the visual representation of a text element or character. + /// + [Serializable] + [UsedByNativeCode] + [StructLayout(LayoutKind.Sequential)] + public class Glyph + { /// - /// The index of the atlas texture that contains this glyph. + /// The index of the glyph in the source font file. /// - public int atlasIndex; + public uint index { get { return m_Index; } set { m_Index = value; } } /// - /// The x position of the glyph in the font atlas texture + /// The metrics that define the size, position and spacing of a glyph when performing text layout. /// - //public int x { get { return m_XMin; } set { m_XMin = value; } } - //[SerializeField] - //[NativeName("x")] - //private int m_XMin; + public GlyphMetrics metrics { get { return m_Metrics; } set { m_Metrics = value; } } /// - /// The y position of the glyph in the font atlas texture. + /// A rectangle that defines the position of a glyph within an atlas texture. /// - //public int y { get { return m_YMin; } set { m_YMin = value; } } - //[SerializeField] - //[NativeName("y")] - //private int m_YMin; + public GlyphRect glyphRect { get { return m_GlyphRect; } set { m_GlyphRect = value; } } /// - /// A Rect that contains the uv coordinates of the glyph in the atlas texture. These values correspond to the xMin, yMin, xMax, yMax positions. + /// The relative scale of the glyph. The default value is 1.0. /// - //public Vector4 uv; + public float scale { get { return m_Scale; } set { m_Scale = value; } } /// - /// The UV coordinate corresponding to the bottom left of the glyph in texture space. + /// The index of the atlas texture that contains this glyph. /// - //public Vector2 uv0 { get { return new Vector2(uv.xMin, uv.yMin); } } + public int atlasIndex { get { return m_AtlasIndex; } set { m_AtlasIndex = value; } } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + [NativeName("index")] + private uint m_Index; + + [SerializeField] + [NativeName("metrics")] + private GlyphMetrics m_Metrics; + + [SerializeField] + [NativeName("glyphRect")] + private GlyphRect m_GlyphRect; + + [SerializeField] + [NativeName("scale")] + private float m_Scale; + + [SerializeField] + [NativeName("atlasIndex")] + private int m_AtlasIndex; /// - /// The UV coordinate corresponding to the top left of the glyph in texture space. + /// Constructor for a new glyph. /// - //public Vector2 uv1 { get { return new Vector2(uv.xMin, uv.yMax); } } + public Glyph() + { + m_Index = 0; + m_Metrics = new GlyphMetrics(); + m_GlyphRect = new GlyphRect(); + m_Scale = 1; + m_AtlasIndex = 0; + } /// - /// The UV coordinate corresponding to the top right of the glyph in texture space. + /// Constructor for a new glyph /// - //public Vector2 uv2 { get { return new Vector2(uv.xMax, uv.yMax); } } + /// Glyph whose values are copied to the new glyph. + public Glyph(Glyph glyph) + { + m_Index = glyph.index; + m_Metrics = glyph.metrics; + m_GlyphRect = glyph.glyphRect; + m_Scale = glyph.scale; + m_AtlasIndex = glyph.atlasIndex; + } /// - /// The UV coordinate corresponding to the bottom right of the glyph in texture space. + /// Constructor for a new glyph /// - //public Vector2 uv3 { get { return new Vector2(uv.xMax, uv.yMin); } } + /// Glyph whose values are copied to the new glyph. + internal Glyph(GlyphMarshallingStruct glyphStruct) + { + m_Index = glyphStruct.index; + m_Metrics = glyphStruct.metrics; + m_GlyphRect = glyphStruct.glyphRect; + m_Scale = glyphStruct.scale; + m_AtlasIndex = glyphStruct.atlasIndex; + } /// - /// Set the uv coordinates of the glyph in the atlas texture. + /// Constructor for new glyph. + /// The scale will be set to a value of 1.0 and atlas index to 0. /// - /// The x position of the glyph in the atlas texture. - /// The y position of the glyph in the atlas texture. - /// The width of the atlas texture. - /// The height of the atlas texture. - //public void SetUV(int x, int y, int texWidth, int texHeight) - //{ - // uv.xMin = x / texWidth; - // uv.xMax = uv.xMin + m_Width / texWidth; - - // uv.xMin = y / texHeight; - // uv.yMax = uv.yMin + m_Height / texHeight; - //} + /// The index of the glyph in the font file. + /// The metrics of the glyph. + /// The GlyphRect defining the position of the glyph in the atlas texture. + public Glyph(uint index, GlyphMetrics metrics, GlyphRect glyphRect) + { + m_Index = index; + m_Metrics = metrics; + m_GlyphRect = glyphRect; + m_Scale = 1; + m_AtlasIndex = 0; + } + + /// + /// Constructor for new glyph. + /// + /// The index of the glyph in the font file. + /// The metrics of the glyph. + /// The GlyphRect defining the position of the glyph in the atlas texture. + /// The relative scale of the glyph. + /// The index of the atlas texture that contains the glyph. + public Glyph(uint index, GlyphMetrics metrics, GlyphRect glyphRect, float scale, int atlasIndex) + { + m_Index = index; + m_Metrics = metrics; + m_GlyphRect = glyphRect; + m_Scale = scale; + m_AtlasIndex = atlasIndex; + } + + /// + /// Compares two glyphs to determine if they have the same values. + /// + /// The glyph to compare with. + /// Returns true if the glyphs have the same values. False if not. + public bool Compare(Glyph other) + { + return index == other.index && + metrics == other.metrics && + glyphRect == other.glyphRect && + scale == other.scale && + atlasIndex == other.atlasIndex; + } } } diff --git a/Modules/TextCore/Managed/OpenTypeCommon.cs b/Modules/TextCore/Managed/OpenTypeCommon.cs new file mode 100644 index 0000000000..c3ce401050 --- /dev/null +++ b/Modules/TextCore/Managed/OpenTypeCommon.cs @@ -0,0 +1,161 @@ +// 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.Runtime.InteropServices; +using UnityEngine.Bindings; +using UnityEngine.Scripting; + + +namespace UnityEngine.TextCore.LowLevel +{ + /// + /// The values used to adjust the position of a glyph or set of glyphs. + /// + [Serializable] + [UsedByNativeCode] + [StructLayout(LayoutKind.Sequential)] + internal struct GlyphValueRecord + { + /// + /// The positional adjustment affecting the horizontal bearing X of the glyph. + /// + public float xPlacement { get { return m_XPlacement; } set { m_XPlacement = value; } } + + /// + /// The positional adjustment affecting the horizontal bearing Y of the glyph. + /// + public float yPlacement { get { return m_YPlacement; } set { m_YPlacement = value; } } + + /// + /// The positional adjustment affecting the horizontal advance of the glyph. + /// + public float xAdvance { get { return m_XAdvance; } set { m_XAdvance = value; } } + + /// + /// The positional adjustment affecting the vertical advance of the glyph. + /// + public float yAdvance { get { return m_YAdvance; } set { m_YAdvance = value; } } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + [NativeName("xPlacement")] + private float m_XPlacement; + + [SerializeField] + [NativeName("yPlacement")] + private float m_YPlacement; + + [SerializeField] + [NativeName("xAdvance")] + private float m_XAdvance; + + [SerializeField] + [NativeName("yAdvance")] + private float m_YAdvance; + + + /// + /// Constructor + /// + /// The positional adjustment affecting the horizontal bearing X of the glyph. + /// The positional adjustment affecting the horizontal bearing Y of the glyph. + /// The positional adjustment affecting the horizontal advance of the glyph. + /// The positional adjustment affecting the vertical advance of the glyph. + public GlyphValueRecord(float xPlacement, float yPlacement, float xAdvance, float yAdvance) + { + m_XPlacement = xPlacement; + m_YPlacement = yPlacement; + m_XAdvance = xAdvance; + m_YAdvance = yAdvance; + } + + public static GlyphValueRecord operator+(GlyphValueRecord a, GlyphValueRecord b) + { + GlyphValueRecord c; + c.m_XPlacement = a.xPlacement + b.xPlacement; + c.m_YPlacement = a.yPlacement + b.yPlacement; + c.m_XAdvance = a.xAdvance + b.xAdvance; + c.m_YAdvance = a.yAdvance + b.yAdvance; + + return c; + } + } + + /// + /// The positional adjustment values of a glyph. + /// + [Serializable] + [UsedByNativeCode] + [StructLayout(LayoutKind.Sequential)] + internal struct GlyphAdjustmentRecord + { + /// + /// The index of the glyph in the source font file. + /// + public uint glyphIndex { get { return m_GlyphIndex; } set { m_GlyphIndex = value; } } + + /// + /// The GlyphValueRecord contains the positional adjustments of the glyph. + /// + public GlyphValueRecord glyphValueRecord { get { return m_GlyphValueRecord; } set { m_GlyphValueRecord = value; } } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + [NativeName("glyphIndex")] + private uint m_GlyphIndex; + + [SerializeField] + [NativeName("glyphValueRecord")] + private GlyphValueRecord m_GlyphValueRecord; + + /// + /// Constructor + /// + /// The index of the glyph in the source font file. + /// The GlyphValueRecord contains the positional adjustments of the glyph. + public GlyphAdjustmentRecord(uint glyphIndex, GlyphValueRecord glyphValueRecord) + { + m_GlyphIndex = glyphIndex; + m_GlyphValueRecord = glyphValueRecord; + } + } + + /// + /// The positional adjustment values for a pair of glyphs. + /// + [Serializable] + [UsedByNativeCode] + [StructLayout(LayoutKind.Sequential)] + internal struct GlyphPairAdjustmentRecord + { + /// + /// Contains the positional adjustment values for the first glyph. + /// + public GlyphAdjustmentRecord firstAdjustmentRecord { get { return m_FirstAdjustmentRecord; } set { m_FirstAdjustmentRecord = value; } } + + /// + /// Contains the positional adjustment values for the second glyph. + /// + public GlyphAdjustmentRecord secondAdjustmentRecord { get { return m_SecondAdjustmentRecord; } set { m_SecondAdjustmentRecord = value; } } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + [NativeName("firstAdjustmentRecord")] + private GlyphAdjustmentRecord m_FirstAdjustmentRecord; + + [SerializeField] + [NativeName("secondAdjustmentRecord")] + private GlyphAdjustmentRecord m_SecondAdjustmentRecord; + } +} diff --git a/Modules/TextCore/ScriptBindings/FontEngine.bindings.cs b/Modules/TextCore/ScriptBindings/FontEngine.bindings.cs index b0b99f22f0..0505978ef4 100644 --- a/Modules/TextCore/ScriptBindings/FontEngine.bindings.cs +++ b/Modules/TextCore/ScriptBindings/FontEngine.bindings.cs @@ -2,6 +2,7 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine.Scripting; using UnityEngine.Bindings; @@ -27,12 +28,11 @@ public enum GlyphLoadFlags //LOAD_LINEAR_DESIGN = 1 << 13, LOAD_NO_AUTOHINT = 1 << 15, /* Bits 16-19 are used by `LOAD_TARGET_' */ - LOAD_COLOR = 1 << 20, + //LOAD_COLOR = 1 << 20, LOAD_COMPUTE_METRICS = 1 << 21, LOAD_BITMAP_METRICS_ONLY = 1 << 22 } - /// /// Rasterizing modes used by the Font Engine to raster glyphs. /// @@ -71,21 +71,34 @@ public enum FontEngineError Invalid_File_Path = 0x1, Invalid_File_Format = 0x2, Invalid_File_Structure = 0x3, + Invalid_File = 0x4, + Invalid_Table = 0x8, // Glyph related errors. Invalid_Glyph_Index = 0x10, Invalid_Character_Code = 0x11, Invalid_Pixel_Size = 0x17, + // + Invalid_Library = 0x21, + + // Font face related errors. + Invalid_Face = 0x23, + + Invalid_Library_or_Face = 0x29, + + // Font atlas generation and glyph rendering related errors. + Atlas_Generation_Cancelled = 0x64, + Invalid_SharedTextureData = 0x65, + // Additional errors codes will be added as necessary to cover new FontEngine features and functionality. } - /// /// Rendering modes used by the Font Engine to render glyphs. /// [UsedByNativeCode] - public enum GlyphRenderModes + public enum GlyphRenderMode { SMOOTH_HINTED = GlyphRasterModes.RASTER_MODE_HINTED | GlyphRasterModes.RASTER_MODE_8BIT | GlyphRasterModes.RASTER_MODE_BITMAP | GlyphRasterModes.RASTER_MODE_1X, SMOOTH = GlyphRasterModes.RASTER_MODE_NO_HINTING | GlyphRasterModes.RASTER_MODE_8BIT | GlyphRasterModes.RASTER_MODE_BITMAP | GlyphRasterModes.RASTER_MODE_1X, @@ -98,17 +111,40 @@ public enum GlyphRenderModes SDF16 = GlyphRasterModes.RASTER_MODE_HINTED | GlyphRasterModes.RASTER_MODE_MONO | GlyphRasterModes.RASTER_MODE_SDF | GlyphRasterModes.RASTER_MODE_16X, SDF32 = GlyphRasterModes.RASTER_MODE_HINTED | GlyphRasterModes.RASTER_MODE_MONO | GlyphRasterModes.RASTER_MODE_SDF | GlyphRasterModes.RASTER_MODE_32X, - SDFAA = GlyphRasterModes.RASTER_MODE_HINTED | GlyphRasterModes.RASTER_MODE_8BIT | GlyphRasterModes.RASTER_MODE_SDFAA | GlyphRasterModes.RASTER_MODE_1X, + SDFAA_HINTED = GlyphRasterModes.RASTER_MODE_HINTED | GlyphRasterModes.RASTER_MODE_8BIT | GlyphRasterModes.RASTER_MODE_SDFAA | GlyphRasterModes.RASTER_MODE_1X, + SDFAA = GlyphRasterModes.RASTER_MODE_NO_HINTING | GlyphRasterModes.RASTER_MODE_8BIT | GlyphRasterModes.RASTER_MODE_SDFAA | GlyphRasterModes.RASTER_MODE_1X, //MSDF = RasterModes.RASTER_MODE_HINTED | RasterModes.RASTER_MODE_8BIT | RasterModes.RASTER_MODE_MSDF | RasterModes.RASTER_MODE_1X, //MSDFA = RasterModes.RASTER_MODE_HINTED | RasterModes.RASTER_MODE_8BIT | RasterModes.RASTER_MODE_MSDFA | RasterModes.RASTER_MODE_1X, } + /// + /// The modes available when packing glyphs into an atlas texture. + /// + [UsedByNativeCode] + public enum GlyphPackingMode + { + BestShortSideFit = 0x0, + BestLongSideFit = 0x1, + BestAreaFit = 0x2, + BottomLeftRule = 0x3, + ContactPointRule = 0x4, + } - [NativeHeader("Modules/TextCore/Native/FontEngine.h")] + [NativeHeader("Modules/TextCore/Native/FontEngine/FontEngine.h")] public sealed class FontEngine { private static readonly FontEngine s_Instance = new FontEngine(); + private static GlyphMarshallingStruct[] s_GlyphMarshallingStruct_IN = new GlyphMarshallingStruct[16]; + private static GlyphMarshallingStruct[] s_GlyphMarshallingStruct_OUT = new GlyphMarshallingStruct[16]; + + private static GlyphRect[] s_FreeGlyphRects = new GlyphRect[16]; + private static GlyphRect[] s_UsedGlyphRects = new GlyphRect[16]; + + private static GlyphPairAdjustmentRecord[] s_GlyphPairAdjustmentRecords; + + private static Dictionary s_GlyphLookupDictionary = new Dictionary(); + internal FontEngine() {} /// @@ -144,6 +180,38 @@ public static FontEngineError DestroyFontEngine() [NativeMethod(Name = "TextCore::FontEngine::DestroyFontEngine", IsFreeFunction = true)] static extern int DestroyFontEngine_Internal(); + /// + /// Force the cancellation of any atlas or glyph rendering process. + /// Used primarily by the Font Asset Creator. + /// + internal static void SendCancellationRequest() + { + SendCancellationRequest_Internal(); + } + + [NativeMethod(Name = "TextCore::FontEngine::SendCancellationRequest", IsFreeFunction = true)] + static extern void SendCancellationRequest_Internal(); + + /// + /// Determines if the font engine is currently in the process of rendering and adding glyphs into an atlas texture. + /// Used primarily by the Font Asset Creator. + /// + internal static extern bool isProcessingDone + { + [NativeMethod(Name = "TextCore::FontEngine::GetIsProcessingDone", IsFreeFunction = true)] + get; + } + + /// + /// Returns the generation progress on glyph packing and rendering. + /// Used primarily by the Font Asset Creator. + /// + internal static extern float generationProgress + { + [NativeMethod(Name = "TextCore::FontEngine::GetGenerationProgress", IsFreeFunction = true)] + get; + } + /// /// Load the source font file at the given file path. /// @@ -180,6 +248,9 @@ public static FontEngineError LoadFontFace(string filePath, int pointSize) /// Returns a value of zero if the font face was loaded successfully. public static FontEngineError LoadFontFace(byte[] sourceFontFile) { + if (sourceFontFile.Length == 0) + return FontEngineError.Invalid_File; + return (FontEngineError)LoadFontFace_FromSourceFontFile_Internal(sourceFontFile); } @@ -195,6 +266,9 @@ public static FontEngineError LoadFontFace(byte[] sourceFontFile) /// Returns a value of zero if the font face was loaded successfully. public static FontEngineError LoadFontFace(byte[] sourceFontFile, int pointSize) { + if (sourceFontFile.Length == 0) + return FontEngineError.Invalid_File; + return (FontEngineError)LoadFontFace_With_Size_FromSourceFontFile_Internal(sourceFontFile, pointSize); } @@ -202,6 +276,35 @@ public static FontEngineError LoadFontFace(byte[] sourceFontFile, int pointSize) static extern int LoadFontFace_With_Size_FromSourceFontFile_Internal(byte[] sourceFontFile, int pointSize); + /// + /// Load the font file from the Unity font's internal font data. Note the Unity font must be set to Dynamic with Include Font Data enabled. + /// + /// The font from which to load the data. + /// Returns a value of zero if the font face was loaded successfully. + public static FontEngineError LoadFontFace(Font font) + { + return (FontEngineError)LoadFontFace_FromFont_Internal(font); + } + + [NativeMethod(Name = "TextCore::FontEngine::LoadFontFace", IsFreeFunction = true)] + static extern int LoadFontFace_FromFont_Internal(Font font); + + + /// + /// Load the font file from the Unity font's internal font data. Note the Unity font must be set to Dynamic with Include Font Data enabled. + /// + /// The font from which to load the data. + /// The point size used to scale the font face. + /// Returns a value of zero if the font face was loaded successfully. + public static FontEngineError LoadFontFace(Font font, int pointSize) + { + return (FontEngineError)LoadFontFace_With_Size_FromFont_Internal(font, pointSize); + } + + [NativeMethod(Name = "TextCore::FontEngine::LoadFontFace", IsFreeFunction = true)] + static extern int LoadFontFace_With_Size_FromFont_Internal(Font font, int pointSize); + + /// /// Set the size of the currently loaded font face. /// @@ -212,17 +315,44 @@ public static FontEngineError SetFaceSize(int pointSize) return (FontEngineError)SetFaceSize_Internal(pointSize); } - [NativeMethod(Name = "TextCore::FontEngine::SetFaceSize", IsFreeFunction = true)] + [NativeMethod(Name = "TextCore::FontEngine::SetFaceSize", IsThreadSafe = true, IsFreeFunction = true)] static extern int SetFaceSize_Internal(int pointSize); + /// + /// Get information about the currently loaded and sized font face. + /// + /// Returns the FaceInfo of the currently loaded font face. + public static FaceInfo GetFaceInfo() + { + FaceInfo faceInfo = new FaceInfo(); + + GetFaceInfo_Internal(ref faceInfo); + + return faceInfo; + } + + [NativeMethod(Name = "TextCore::FontEngine::GetFaceInfo", IsThreadSafe = true, IsFreeFunction = true)] + static extern int GetFaceInfo_Internal(ref FaceInfo faceInfo); + + /// /// Get the index of the glyph for the character mapped at Unicode value. /// /// The Unicode value of the character for which to lookup the glyph index. /// Returns the index of the glyph used by the character using the Unicode value. Returns zero if no glyph exists for the given Unicode value. - [NativeMethod(Name = "TextCore::FontEngine::GetGlyphIndex", IsFreeFunction = true)] - public static extern int GetGlyphIndex(uint unicode); + [NativeMethod(Name = "TextCore::FontEngine::GetGlyphIndex", IsThreadSafe = true, IsFreeFunction = true)] + internal static extern uint GetGlyphIndex(uint unicode); + + + /// + /// Try to get the glyph index for the character at the given Unicode value. + /// + /// The unicode value of the character for which to lookup the glyph index. + /// The index of the glyph for the given unicode character or the .notdef glyph (index 0) if no glyph is available for the given Unicode value. + /// Returns true if the given unicode has a glyph index. + [NativeMethod(Name = "TextCore::FontEngine::TryGetGlyphIndex", IsThreadSafe = true, IsFreeFunction = true)] + public static extern bool TryGetGlyphIndex(uint unicode, out uint glyphIndex); /// @@ -236,7 +366,7 @@ internal static FontEngineError LoadGlyph(uint unicode, GlyphLoadFlags flags) return (FontEngineError)LoadGlyph_Internal(unicode, flags); } - [NativeMethod(Name = "TextCore::FontEngine::LoadGlyph", IsFreeFunction = true)] + [NativeMethod(Name = "TextCore::FontEngine::LoadGlyph", IsThreadSafe = true, IsFreeFunction = true)] static extern int LoadGlyph_Internal(uint unicode, GlyphLoadFlags loadFlags); @@ -249,13 +379,23 @@ internal static FontEngineError LoadGlyph(uint unicode, GlyphLoadFlags flags) /// Returns true if a glyph exists for the given unicode value. Otherwise returns false. public static bool TryGetGlyphWithUnicodeValue(uint unicode, GlyphLoadFlags flags, out Glyph glyph) { - glyph = new Glyph(); + GlyphMarshallingStruct glyphStruct = new GlyphMarshallingStruct(); + + if (TryGetGlyphWithUnicodeValue_Internal(unicode, flags, ref glyphStruct)) + { + glyph = new Glyph(glyphStruct); - return TryGetGlyphWithUnicodeValue_Internal(unicode, flags, glyph); + return true; + } + + // Set glyph to null if no glyph exists for the given unicode value. + glyph = null; + + return false; } - [NativeMethod(Name = "TextCore::FontEngine::TryGetGlyphWithUnicodeValue", IsFreeFunction = true)] - static extern bool TryGetGlyphWithUnicodeValue_Internal(uint unicode, GlyphLoadFlags loadFlags, [Out] Glyph glyph); + [NativeMethod(Name = "TextCore::FontEngine::TryGetGlyphWithUnicodeValue", IsThreadSafe = true, IsFreeFunction = true)] + static extern bool TryGetGlyphWithUnicodeValue_Internal(uint unicode, GlyphLoadFlags loadFlags, ref GlyphMarshallingStruct glyphStruct); /// @@ -267,46 +407,516 @@ public static bool TryGetGlyphWithUnicodeValue(uint unicode, GlyphLoadFlags flag /// Returns true if a glyph exists at the given index. Otherwise returns false. public static bool TryGetGlyphWithIndexValue(uint glyphIndex, GlyphLoadFlags flags, out Glyph glyph) { - glyph = new Glyph(); + GlyphMarshallingStruct glyphStruct = new GlyphMarshallingStruct(); + + if (TryGetGlyphWithIndexValue_Internal(glyphIndex, flags, ref glyphStruct)) + { + glyph = new Glyph(glyphStruct); - return TryGetGlyphWithIndexValue_Internal(glyphIndex, flags, glyph); + return true; + } + + // Set glyph to null if no glyph exists for the given unicode value. + glyph = null; + + return false; + } + + [NativeMethod(Name = "TextCore::FontEngine::TryGetGlyphWithIndexValue", IsThreadSafe = true, IsFreeFunction = true)] + static extern bool TryGetGlyphWithIndexValue_Internal(uint glyphIndex, GlyphLoadFlags loadFlags, ref GlyphMarshallingStruct glyphStruct); + + + /// + /// Try to pack the given glyph into the given texture width and height. + /// + /// The glyph to try to pack. + /// The padding between this glyph and other glyphs. + /// The packing algorithm used to pack the glyphs. + /// The glyph rendering mode. + /// The width of the target atlas texture. + /// The height of the target atlas texture. + /// List of GlyphRects representing the available space in the atlas. + /// List of GlyphRects representing the occupied space in the atlas. + /// + internal static bool TryPackGlyphInAtlas(Glyph glyph, int padding, GlyphPackingMode packingMode, GlyphRenderMode renderMode, int width, int height, List freeGlyphRects, List usedGlyphRects) + { + GlyphMarshallingStruct glyphStruct = new GlyphMarshallingStruct(glyph); + + int freeGlyphRectCount = freeGlyphRects.Count; + int usedGlyphRectCount = usedGlyphRects.Count; + int totalGlyphRects = freeGlyphRectCount + usedGlyphRectCount; + + // Make sure marshalling arrays allocations are appropriate. + if (s_FreeGlyphRects.Length < totalGlyphRects || s_UsedGlyphRects.Length < totalGlyphRects) + { + int newSize = Mathf.NextPowerOfTwo(totalGlyphRects + 1); + s_FreeGlyphRects = new GlyphRect[newSize]; + s_UsedGlyphRects = new GlyphRect[newSize]; + } + + // Copy glyph rect data to marshalling arrays. + int glyphRectCount = Mathf.Max(freeGlyphRectCount, usedGlyphRectCount); + for (int i = 0; i < glyphRectCount; i++) + { + if (i < freeGlyphRectCount) + s_FreeGlyphRects[i] = freeGlyphRects[i]; + + if (i < usedGlyphRectCount) + s_UsedGlyphRects[i] = usedGlyphRects[i]; + } + + if (TryPackGlyphInAtlas_Internal(ref glyphStruct, padding, packingMode, renderMode, width, height, s_FreeGlyphRects, ref freeGlyphRectCount, s_UsedGlyphRects, ref usedGlyphRectCount)) + { + // Copy new glyph position to source glyph. + glyph.glyphRect = glyphStruct.glyphRect; + + freeGlyphRects.Clear(); + usedGlyphRects.Clear(); + + // Copy marshalled glyph rect data + glyphRectCount = Mathf.Max(freeGlyphRectCount, usedGlyphRectCount); + for (int i = 0; i < glyphRectCount; i++) + { + if (i < freeGlyphRectCount) + freeGlyphRects.Add(s_FreeGlyphRects[i]); + + if (i < usedGlyphRectCount) + usedGlyphRects.Add(s_UsedGlyphRects[i]); + } + + return true; + } + + return false; + } + + [NativeMethod(Name = "TextCore::FontEngine::TryPackGlyph", IsThreadSafe = true, IsFreeFunction = true)] + extern static bool TryPackGlyphInAtlas_Internal(ref GlyphMarshallingStruct glyph, int padding, GlyphPackingMode packingMode, GlyphRenderMode renderMode, int width, int height, + [Out] GlyphRect[] freeGlyphRects, ref int freeGlyphRectCount, [Out] GlyphRect[] usedGlyphRects, ref int usedGlyphRectCount); + + + /// + /// Pack glyphs in the given atlas size. + /// + /// Glyphs to pack in atlas. + /// Glyphs packed in atlas. + /// The padding between glyphs. + /// The packing algorithm used to pack the glyphs. + /// The glyph rendering mode. + /// The width of the target atlas texture. + /// The height of the target atlas texture. + /// List of GlyphRects representing the available space in the atlas. + /// List of GlyphRects representing the occupied space in the atlas. + /// + internal static bool TryPackGlyphsInAtlas(List glyphsToAdd, List glyphsAdded, int padding, GlyphPackingMode packingMode, GlyphRenderMode renderMode, int width, int height, List freeGlyphRects, List usedGlyphRects) + { + // Determine potential total allocations required for glyphs and glyph rectangles. + int glyphsToAddCount = glyphsToAdd.Count; + int glyphsAddedCount = glyphsAdded.Count; + int freeGlyphRectCount = freeGlyphRects.Count; + int usedGlyphRectCount = usedGlyphRects.Count; + int totalCount = glyphsToAddCount + glyphsAddedCount + freeGlyphRectCount + usedGlyphRectCount; + + // Make sure marshaling arrays allocations are appropriate. + if (s_GlyphMarshallingStruct_IN.Length < totalCount || s_GlyphMarshallingStruct_OUT.Length < totalCount || s_FreeGlyphRects.Length < totalCount || s_UsedGlyphRects.Length < totalCount) + { + int newSize = Mathf.NextPowerOfTwo(totalCount + 1); + s_GlyphMarshallingStruct_IN = new GlyphMarshallingStruct[newSize]; + s_GlyphMarshallingStruct_OUT = new GlyphMarshallingStruct[newSize]; + s_FreeGlyphRects = new GlyphRect[newSize]; + s_UsedGlyphRects = new GlyphRect[newSize]; + } + + s_GlyphLookupDictionary.Clear(); + + // Copy glyph data into appropriate marshaling array. + for (int i = 0; i < totalCount; i++) + { + if (i < glyphsToAddCount) + { + GlyphMarshallingStruct glyphStruct = new GlyphMarshallingStruct(glyphsToAdd[i]); + + s_GlyphMarshallingStruct_IN[i] = glyphStruct; + + // Add reference to glyph in lookup dictionary + if (s_GlyphLookupDictionary.ContainsKey(glyphStruct.index) == false) + s_GlyphLookupDictionary.Add(glyphStruct.index, glyphsToAdd[i]); + } + + if (i < glyphsAddedCount) + { + GlyphMarshallingStruct glyphStruct = new GlyphMarshallingStruct(glyphsAdded[i]); + + s_GlyphMarshallingStruct_OUT[i] = glyphStruct; + + // Add reference to glyph in lookup dictionary + if (s_GlyphLookupDictionary.ContainsKey(glyphStruct.index) == false) + s_GlyphLookupDictionary.Add(glyphStruct.index, glyphsAdded[i]); + } + + if (i < freeGlyphRectCount) + s_FreeGlyphRects[i] = freeGlyphRects[i]; + + if (i < usedGlyphRectCount) + s_UsedGlyphRects[i] = usedGlyphRects[i]; + } + + bool allGlyphsIncluded = TryPackGlyphsInAtlas_Internal(s_GlyphMarshallingStruct_IN, ref glyphsToAddCount, s_GlyphMarshallingStruct_OUT, ref glyphsAddedCount, + padding, packingMode, renderMode, width, height, + s_FreeGlyphRects, ref freeGlyphRectCount, s_UsedGlyphRects, ref usedGlyphRectCount); + + // Clear lists and / or re-allocate arrays. + glyphsToAdd.Clear(); + glyphsAdded.Clear(); + freeGlyphRects.Clear(); + usedGlyphRects.Clear(); + + // Copy marshaled glyph data back into the appropriate lists. + for (int i = 0; i < totalCount; i++) + { + if (i < glyphsToAddCount) + { + GlyphMarshallingStruct glyphStruct = s_GlyphMarshallingStruct_IN[i]; + Glyph glyph = s_GlyphLookupDictionary[glyphStruct.index]; + + // Note: In theory, only new glyphRect x and y need to be copied. + glyph.metrics = glyphStruct.metrics; + glyph.glyphRect = glyphStruct.glyphRect; + glyph.scale = glyphStruct.scale; + glyph.atlasIndex = glyphStruct.atlasIndex; + + glyphsToAdd.Add(glyph); + } + + if (i < glyphsAddedCount) + { + GlyphMarshallingStruct glyphStruct = s_GlyphMarshallingStruct_OUT[i]; + Glyph glyph = s_GlyphLookupDictionary[glyphStruct.index]; + + glyph.metrics = glyphStruct.metrics; + glyph.glyphRect = glyphStruct.glyphRect; + glyph.scale = glyphStruct.scale; + glyph.atlasIndex = glyphStruct.atlasIndex; + + glyphsAdded.Add(glyph); + } + + if (i < freeGlyphRectCount) + { + freeGlyphRects.Add(s_FreeGlyphRects[i]); + } + + if (i < usedGlyphRectCount) + { + usedGlyphRects.Add(s_UsedGlyphRects[i]); + } + } + + return allGlyphsIncluded; } - [NativeMethod(Name = "TextCore::FontEngine::TryGetGlyphWithIndexValue", IsFreeFunction = true)] - static extern bool TryGetGlyphWithIndexValue_Internal(uint glyphIndex, GlyphLoadFlags loadFlags, [Out] Glyph glyph); + [NativeMethod(Name = "TextCore::FontEngine::TryPackGlyphs", IsThreadSafe = true, IsFreeFunction = true)] + extern static bool TryPackGlyphsInAtlas_Internal([Out] GlyphMarshallingStruct[] glyphsToAdd, ref int glyphsToAddCount, [Out] GlyphMarshallingStruct[] glyphsAdded, ref int glyphsAddedCount, + int padding, GlyphPackingMode packingMode, GlyphRenderMode renderMode, int width, int height, + [Out] GlyphRect[] freeGlyphRects, ref int freeGlyphRectCount, [Out] GlyphRect[] usedGlyphRects, ref int usedGlyphRectCount); /// - /// Try rasterizing and adding the given glyph to the provided texture. + /// Render and add glyph to the provided texture. /// /// The Glyph that should be added into the provided texture. /// The padding value around the glyph. /// The Rendering Mode for the Glyph. /// The Texture to which the glyph should be added. /// Returns a value of zero if the glyph was successfully added to the texture. - public static FontEngineError AddGlyphToTexture(Glyph glyph, int padding, GlyphRenderModes renderMode, Texture2D texture) + internal static FontEngineError RenderGlyphToTexture(Glyph glyph, int padding, GlyphRenderMode renderMode, Texture2D texture) { - return (FontEngineError)AddGlyphToTexture_Internal(glyph, padding, renderMode, texture); + GlyphMarshallingStruct glyphStruct = new GlyphMarshallingStruct(glyph); + + return (FontEngineError)RenderGlyphToTexture_Internal(glyphStruct, padding, renderMode, texture); } - [NativeMethod(Name = "TextCore::FontEngine::AddGlyphToTexture", IsFreeFunction = true)] - extern static int AddGlyphToTexture_Internal(Glyph glyph, int padding, GlyphRenderModes renderMode, Texture2D texture); + [NativeMethod(Name = "TextCore::FontEngine::RenderGlyphToTexture", IsFreeFunction = true)] + extern static int RenderGlyphToTexture_Internal(GlyphMarshallingStruct glyphStruct, int padding, GlyphRenderMode renderMode, Texture2D texture); /// - /// Try rasterizing and adding the given list of glyphs to the provided texture. + /// Render and add the glyphs in the provided list to the texture. /// - /// The list of glyphs to be added into the provided texture. + /// The list of glyphs to be rendered and added to the provided texture. /// The padding value around the glyphs. - /// The rendering mode used rasterize the glyphs. + /// The rendering mode used to rasterize the glyphs. /// Returns a value of zero if the glyphs were successfully added to the texture. /// - public static FontEngineError AddGlyphsToTexture(Glyph[] glyphs, int padding, GlyphRenderModes renderMode, Texture2D texture) + internal static FontEngineError RenderGlyphsToTexture(List glyphs, int padding, GlyphRenderMode renderMode, Texture2D texture) + { + int glyphCount = glyphs.Count; + + // Make sure marshaling arrays allocations are appropriate. + if (s_GlyphMarshallingStruct_IN.Length < glyphCount) + { + int newSize = Mathf.NextPowerOfTwo(glyphCount + 1); + s_GlyphMarshallingStruct_IN = new GlyphMarshallingStruct[newSize]; + } + + // Copy data to marshalling buffers + for (int i = 0; i < glyphCount; i++) + s_GlyphMarshallingStruct_IN[i] = new GlyphMarshallingStruct(glyphs[i]); + + // Call extern function to render and add glyphs to texture. + int error = RenderGlyphsToTexture_Internal(s_GlyphMarshallingStruct_IN, glyphCount, padding, renderMode, texture); + + return (FontEngineError)error; + } + + [NativeMethod(Name = "TextCore::FontEngine::RenderGlyphsToTexture", IsFreeFunction = true)] + extern static int RenderGlyphsToTexture_Internal(GlyphMarshallingStruct[] glyphs, int glyphCount, int padding, GlyphRenderMode renderMode, Texture2D texture); + + + internal static FontEngineError RenderGlyphsToTexture(List glyphs, int padding, GlyphRenderMode renderMode, byte[] texBuffer, int texWidth, int texHeight) + { + int glyphCount = glyphs.Count; + + // Make sure marshaling arrays allocations are appropriate. + if (s_GlyphMarshallingStruct_IN.Length < glyphCount) + { + int newSize = Mathf.NextPowerOfTwo(glyphCount + 1); + s_GlyphMarshallingStruct_IN = new GlyphMarshallingStruct[newSize]; + } + + // Copy data to marshalling buffers + for (int i = 0; i < glyphCount; i++) + s_GlyphMarshallingStruct_IN[i] = new GlyphMarshallingStruct(glyphs[i]); + + int error = RenderGlyphsToTextureBuffer_Internal(s_GlyphMarshallingStruct_IN, glyphCount, padding, renderMode, texBuffer, texWidth, texHeight); + + return (FontEngineError)error; + } + + [NativeMethod(Name = "TextCore::FontEngine::RenderGlyphsToTextureBuffer", IsThreadSafe = true, IsFreeFunction = true)] + extern static int RenderGlyphsToTextureBuffer_Internal(GlyphMarshallingStruct[] glyphs, int glyphCount, int padding, GlyphRenderMode renderMode, [Out] byte[] texBuffer, int texWidth, int texHeight); + + + /// + /// Internal function used to render and add glyphs to the cached shared texture data from outside the main thread. + /// It is necessary to use SetSharedTextureData(texture) prior to calling this function. + /// + /// The list of glyphs to be added into the provided texture. + /// The padding value around the glyphs. + /// The rendering mode used to rasterize the glyphs. + /// + internal static FontEngineError RenderGlyphsToSharedTexture(List glyphs, int padding, GlyphRenderMode renderMode) + { + int glyphCount = glyphs.Count; + + // Make sure marshaling arrays allocations are appropriate. + if (s_GlyphMarshallingStruct_IN.Length < glyphCount) + { + int newSize = Mathf.NextPowerOfTwo(glyphCount + 1); + s_GlyphMarshallingStruct_IN = new GlyphMarshallingStruct[newSize]; + } + + // Copy data to marshalling buffers + for (int i = 0; i < glyphCount; i++) + s_GlyphMarshallingStruct_IN[i] = new GlyphMarshallingStruct(glyphs[i]); + + int error = RenderGlyphsToSharedTexture_Internal(s_GlyphMarshallingStruct_IN, glyphCount, padding, renderMode); + + return (FontEngineError)error; + } + + [NativeMethod(Name = "TextCore::FontEngine::RenderGlyphsToSharedTexture", IsThreadSafe = true, IsFreeFunction = true)] + extern static int RenderGlyphsToSharedTexture_Internal(GlyphMarshallingStruct[] glyphs, int glyphCount, int padding, GlyphRenderMode renderMode); + + + /// + /// Internal function used to get a reference to the shared texture data which is required for accessing the texture data outside of the main thread. + /// + [NativeMethod(Name = "TextCore::FontEngine::SetSharedTextureData", IsFreeFunction = true)] + internal extern static void SetSharedTexture(Texture2D texture); + + + /// + /// Internal function used to release the shared texture data. + /// + [NativeMethod(Name = "TextCore::FontEngine::ReleaseSharedTextureData", IsThreadSafe = true, IsFreeFunction = true)] + internal extern static void ReleaseSharedTexture(); + + + internal static bool TryAddGlyphToTexture(uint glyphIndex, int padding, GlyphPackingMode packingMode, List freeGlyphRects, List usedGlyphRects, GlyphRenderMode renderMode, Texture2D texture, out Glyph glyph) { - return (FontEngineError)AddGlyphsToTextureFromArray_Internal(glyphs, padding, renderMode, texture); + // Determine potential total allocations required for glyphs and glyph rectangles. + int freeGlyphRectCount = freeGlyphRects.Count; + int usedGlyphRectCount = usedGlyphRects.Count; + int totalGlyphRects = freeGlyphRectCount + usedGlyphRectCount; + + // Make sure marshalling arrays allocations are appropriate. + if (s_FreeGlyphRects.Length < totalGlyphRects || s_UsedGlyphRects.Length < totalGlyphRects) + { + int newSize = Mathf.NextPowerOfTwo(totalGlyphRects + 1); + s_FreeGlyphRects = new GlyphRect[newSize]; + s_UsedGlyphRects = new GlyphRect[newSize]; + } + + // Copy glyph rect data to marshalling arrays. + int glyphRectCount = Mathf.Max(freeGlyphRectCount, usedGlyphRectCount); + for (int i = 0; i < glyphRectCount; i++) + { + if (i < freeGlyphRectCount) + s_FreeGlyphRects[i] = freeGlyphRects[i]; + + if (i < usedGlyphRectCount) + s_UsedGlyphRects[i] = usedGlyphRects[i]; + } + + GlyphMarshallingStruct glyphStruct; + + // Marshall data over to the native side. + if (TryAddGlyphToTexture_Internal(glyphIndex, padding, packingMode, s_FreeGlyphRects, ref freeGlyphRectCount, s_UsedGlyphRects, ref usedGlyphRectCount, renderMode, texture, out glyphStruct)) + { + // Copy marshalled data over to new glyph. + glyph = new Glyph(glyphStruct); + + freeGlyphRects.Clear(); + usedGlyphRects.Clear(); + + // Copy marshalled free and used GlyphRect data over. + glyphRectCount = Mathf.Max(freeGlyphRectCount, usedGlyphRectCount); + for (int i = 0; i < glyphRectCount; i++) + { + if (i < freeGlyphRectCount) + freeGlyphRects.Add(s_FreeGlyphRects[i]); + + if (i < usedGlyphRectCount) + usedGlyphRects.Add(s_UsedGlyphRects[i]); + } + + return true; + } + + glyph = null; + + return false; } - [NativeMethod(Name = "TextCore::FontEngine::AddGlyphsToTexture", IsFreeFunction = true)] - extern static int AddGlyphsToTextureFromArray_Internal(Glyph[] glyphs, int padding, GlyphRenderModes renderMode, Texture2D texture); + [NativeMethod(Name = "TextCore::FontEngine::TryAddGlyphToTexture", IsThreadSafe = true, IsFreeFunction = true)] + extern static bool TryAddGlyphToTexture_Internal(uint glyphIndex, int padding, + GlyphPackingMode packingMode, [Out] GlyphRect[] freeGlyphRects, ref int freeGlyphRectCount, [Out] GlyphRect[] usedGlyphRects, ref int usedGlyphRectCount, + GlyphRenderMode renderMode, Texture2D texture, out GlyphMarshallingStruct glyph); + + + /// + /// Internal function used to retrieve positional adjustments for pairs of glyphs. + /// + /// List of glyph indexes to check for potential positional adjustment records. + /// Array containing the positional adjustments for pairs of glyphs. + internal static GlyphPairAdjustmentRecord[] GetGlyphPairAdjustmentTable(uint[] glyphIndexes) + { + int maxGlyphPairAdjustmentRecords = glyphIndexes.Length * glyphIndexes.Length; + + if (s_GlyphPairAdjustmentRecords == null || s_GlyphPairAdjustmentRecords.Length < maxGlyphPairAdjustmentRecords) + { + s_GlyphPairAdjustmentRecords = new GlyphPairAdjustmentRecord[maxGlyphPairAdjustmentRecords]; + } + + int adjustmentRecordCount; + if (GetGlyphPairAdjustmentTable_Internal(glyphIndexes, s_GlyphPairAdjustmentRecords, out adjustmentRecordCount) != 0) + { + // TODO: Add debug warning messages. + return null; + } + + GlyphPairAdjustmentRecord[] pairAdjustmentRecords = new GlyphPairAdjustmentRecord[adjustmentRecordCount]; + + for (int i = 0; i < adjustmentRecordCount; i++) + { + pairAdjustmentRecords[i] = s_GlyphPairAdjustmentRecords[i]; + } + + return pairAdjustmentRecords; + } + + /// + /// + /// + [NativeMethod(Name = "TextCore::FontEngine::GetGlyphPairAdjustmentTable", IsFreeFunction = true)] + extern static int GetGlyphPairAdjustmentTable_Internal(uint[] glyphIndexes, [Out] GlyphPairAdjustmentRecord[] glyphPairAdjustmentRecords, out int adjustmentRecordCount); + + // ================================================ + // Experimental / Testing / Benchmarking Functions + // ================================================ + + /// + /// Internal function used for testing rasterizing of shapes and glyphs. + /// + /// Texture containing the source shape to raster. + /// Padding value. + /// The rendering mode. + /// Texture containing the rastered shape. + [NativeMethod(Name = "TextCore::FontEngine::RenderToTexture", IsFreeFunction = true)] + internal extern static void RenderBufferToTexture(Texture2D srcTexture, int padding, GlyphRenderMode renderMode, Texture2D dstTexture); + + /* + [NativeMethod(Name = "TextCore::FontEngine::ModifyGlyph", IsFreeFunction = true)] + extern public static void ModifyGlyph([Out] Glyph glyph); + + /// + /// + /// + /// + public static void ModifyGlyphStruct(Glyph glyph) + { + GlyphMarshallingStruct glyphStruct = new GlyphMarshallingStruct(glyph); + + ModifyGlyph_Internal(ref glyphStruct); + + glyph.metrics = glyphStruct.metrics; + glyph.glyphRect = glyphStruct.glyphRect; + glyph.scale = glyphStruct.scale; + glyph.atlasIndex = glyphStruct.atlasIndex; + } + + [NativeMethod(Name = "TextCore::FontEngine::ModifyGlyph", IsThreadSafe = true, IsFreeFunction = true)] + extern static void ModifyGlyph_Internal(ref GlyphMarshallingStruct glyphs); + + /// + /// + /// + /// + [NativeMethod(Name = "TextCore::FontEngine::ModifyGlyphMarshallingStruct", IsFreeFunction = true)] + extern public static void ModifyGlyphMarshallingStruct(GlyphMarshallingStruct[] glyph); + + /// + /// + /// + /// + [NativeMethod(Name = "TextCore::FontEngine::ModifyGlyphMarshallingStruct", IsFreeFunction = true)] + extern public static void ModifyGlyphMarshallingStructArray(GlyphMarshallingStruct[] glyph); + + /// + /// + /// + /// + [NativeMethod(Name = "TextCore::FontEngine::ModifyGlyphs", IsFreeFunction = true)] + extern public static void ModifyGlyphStructArray([Out] GlyphMarshallingStruct[] glyph); + + /// + /// + /// + /// + [NativeMethod(Name = "TextCore::FontEngine::ModifyGlyphs", IsFreeFunction = true)] + extern public static void ModifyGlyphArray([Out] Glyph[] glyph); + + [NativeMethod(Name = "TextCore::FontEngine::AccessFont", IsFreeFunction = true)] + extern public static void AccessFont(Font font); + */ + } + + internal struct FontEngineUtilities + { + internal static bool Approximately(float a, float b) + { + return Mathf.Abs(a - b) < 0.001f; + } } } diff --git a/Modules/TextCore/ScriptBindings/GlyphMarshallingStruct.cs b/Modules/TextCore/ScriptBindings/GlyphMarshallingStruct.cs new file mode 100644 index 0000000000..f293121789 --- /dev/null +++ b/Modules/TextCore/ScriptBindings/GlyphMarshallingStruct.cs @@ -0,0 +1,74 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System.Runtime.InteropServices; +using UnityEngine.Bindings; +using UnityEngine.Scripting; + + +namespace UnityEngine.TextCore.LowLevel +{ + /// + /// Structure used for marshalling glyphs between managed and native code. + /// + [UsedByNativeCode] + [StructLayout(LayoutKind.Sequential)] + internal struct GlyphMarshallingStruct + { + /// + /// The index of the glyph in the source font file. + /// + public uint index; + + /// + /// Metrics defining the size, positioning and spacing of a glyph when doing text layout. + /// + public GlyphMetrics metrics; + + /// + /// A rectangle that defines the position of a glyph within an atlas texture. + /// + public GlyphRect glyphRect; + + /// + /// The relative scale of the text element. The default value is 1.0. + /// + public float scale; + + /// + /// The index of the atlas texture that contains this glyph. + /// + public int atlasIndex; + + /// + /// Constructor for a new glyph + /// + /// Glyph whose values are copied to the new glyph. + public GlyphMarshallingStruct(Glyph glyph) + { + this.index = glyph.index; + this.metrics = glyph.metrics; + this.glyphRect = glyph.glyphRect; + this.scale = glyph.scale; + this.atlasIndex = glyph.atlasIndex; + } + + /// + /// Constructor for new glyph + /// + /// The index of the glyph in the font file. + /// The metrics of the glyph. + /// A rectangle defining the position of the glyph in the atlas texture. + /// The relative scale of the glyph. + /// The index of the atlas texture that contains the glyph. + public GlyphMarshallingStruct(uint index, GlyphMetrics metrics, GlyphRect glyphRect, float scale, int atlasIndex) + { + this.index = index; + this.metrics = metrics; + this.glyphRect = glyphRect; + this.scale = scale; + this.atlasIndex = atlasIndex; + } + } +} diff --git a/Modules/TextRendering/TextRendering.bindings.cs b/Modules/TextRendering/TextRendering.bindings.cs index 63d1ff0d0b..d2d1875ef8 100644 --- a/Modules/TextRendering/TextRendering.bindings.cs +++ b/Modules/TextRendering/TextRendering.bindings.cs @@ -365,7 +365,14 @@ public static int GetMaxVertsForString(string str) } internal static extern Font GetDefault(); - public extern bool HasCharacter(char c); + + public bool HasCharacter(char c) + { + return HasCharacter((int)c); + } + + private extern bool HasCharacter(int c); + public static extern string[] GetOSInstalledFontNames(); private static extern void Internal_CreateFont([Writable] Font self, string name); diff --git a/Modules/Tilemap/Managed/GridBrushBase.cs b/Modules/Tilemap/Managed/GridBrushBase.cs index 73c38221d0..9b7bf651bb 100644 --- a/Modules/Tilemap/Managed/GridBrushBase.cs +++ b/Modules/Tilemap/Managed/GridBrushBase.cs @@ -49,5 +49,8 @@ public virtual void Pick(GridLayout gridLayout, GameObject brushTarget, BoundsIn public virtual void Move(GridLayout gridLayout, GameObject brushTarget, BoundsInt from, BoundsInt to) {} public virtual void MoveStart(GridLayout gridLayout, GameObject brushTarget, BoundsInt position) {} public virtual void MoveEnd(GridLayout gridLayout, GameObject brushTarget, BoundsInt position) {} + + public virtual void ChangeZPosition(int change) {} + public virtual void ResetZPosition() {} } } diff --git a/Modules/Tilemap/ScriptBindings/Tilemap.bindings.cs b/Modules/Tilemap/ScriptBindings/Tilemap.bindings.cs index 2fbe0c30a8..26d704374f 100644 --- a/Modules/Tilemap/ScriptBindings/Tilemap.bindings.cs +++ b/Modules/Tilemap/ScriptBindings/Tilemap.bindings.cs @@ -258,6 +258,42 @@ public void EditorPreviewBoxFill(Vector3Int position, Object tile, int startX, i [NativeMethod(Name = "ClearAllEditorPreviewTileAssets")] public extern void ClearAllEditorPreviewTiles(); + + [RequiredByNativeCode] + internal struct SyncTile + { + public Vector3Int m_Position; + public TileBase m_Tile; + public TileData m_TileData; + } + + internal static event Action tilemapTileChanged; + + internal static void SetSyncTileCallback(Action callback) + { + Tilemap.tilemapTileChanged += callback; + } + + internal static void RemoveSyncTileCallback(Action callback) + { + Tilemap.tilemapTileChanged -= callback; + } + + [RequiredByNativeCode] + private static bool HasSyncTileCallback() + { + return (Tilemap.tilemapTileChanged != null); + } + + [RequiredByNativeCode] + private void DoSyncTileCallback(SyncTile[] syncTiles) + { + if (Tilemap.tilemapTileChanged == null) + return; + + Tilemap.tilemapTileChanged(this, syncTiles); + } + } [RequireComponent(typeof(Tilemap))] diff --git a/Modules/TilemapEditor/Editor/Managed/Grid/GridBrush.cs b/Modules/TilemapEditor/Editor/Managed/Grid/GridBrush.cs index 9e14183cba..43d0696099 100644 --- a/Modules/TilemapEditor/Editor/Managed/Grid/GridBrush.cs +++ b/Modules/TilemapEditor/Editor/Managed/Grid/GridBrush.cs @@ -5,6 +5,7 @@ using System; using UnityEngine.Tilemaps; using UnityEngine; +using System.Collections; namespace UnityEditor { @@ -22,6 +23,9 @@ public class GridBrush : GridBrushBase [HideInInspector] private Vector3Int m_Pivot; + private ArrayList m_Locations; + private ArrayList m_Tiles; + private static readonly Matrix4x4 s_Clockwise = new Matrix4x4(new Vector4(0f, 1f, 0f, 0f), new Vector4(-1f, 0f, 0f, 0f), new Vector4(0f, 0f, 1f, 0f), new Vector4(0f, 0f, 0f, 1f)); private static readonly Matrix4x4 s_CounterClockwise = new Matrix4x4(new Vector4(0f, -1f, 0f, 0f), new Vector4(1f, 0f, 0f, 0f), new Vector4(0f, 0f, 1f, 0f), new Vector4(0f, 0f, 0f, 1f)); private static readonly Matrix4x4 s_180Rotate = new Matrix4x4(new Vector4(-1f, 0f, 0f, 0f), new Vector4(0f, -1f, 0f, 0f), new Vector4(0f, 0f, 1f, 0f), new Vector4(0f, 0f, 0f, 1f)); @@ -31,6 +35,26 @@ public class GridBrush : GridBrushBase public BrushCell[] cells { get { return m_Cells; } } public int cellCount { get { return m_Cells != null ? m_Cells.Length : 0; } } + private ArrayList locations + { + get + { + if (m_Locations == null) + m_Locations = new ArrayList(); + return m_Locations; + } + } + + private ArrayList tiles + { + get + { + if (m_Tiles == null) + m_Tiles = new ArrayList(); + return m_Tiles; + } + } + public GridBrush() { Init(Vector3Int.one, Vector3Int.zero); @@ -57,14 +81,6 @@ public override void Paint(GridLayout gridLayout, GameObject brushTarget, Vector BoxFill(gridLayout, brushTarget, bounds); } - private void PaintCell(Vector3Int position, Tilemap tilemap, BrushCell cell) - { - if (cell.tile != null) - { - SetTilemapCell(tilemap, position, cell.tile, cell.matrix, cell.color); - } - } - public override void Erase(GridLayout gridLayout, GameObject brushTarget, Vector3Int position) { Vector3Int min = position - pivot; @@ -72,22 +88,37 @@ public override void Erase(GridLayout gridLayout, GameObject brushTarget, Vector BoxErase(gridLayout, brushTarget, bounds); } - private void EraseCell(Vector3Int position, Tilemap tilemap) - { - ClearTilemapCell(tilemap, position); - } - public override void BoxFill(GridLayout gridLayout, GameObject brushTarget, BoundsInt position) { if (brushTarget == null) return; Tilemap map = brushTarget.GetComponent(); + if (map == null) + return; + + locations.Clear(); + tiles.Clear(); foreach (Vector3Int location in position.allPositionsWithin) { Vector3Int local = location - position.min; BrushCell cell = m_Cells[GetCellIndexWrapAround(local.x, local.y, local.z)]; - PaintCell(location, map, cell); + if (cell.tile == null) + continue; + + locations.Add(location); + tiles.Add(cell.tile); + } + map.SetTiles((Vector3Int[])locations.ToArray(typeof(Vector3Int)), (TileBase[])tiles.ToArray(typeof(TileBase))); + foreach (Vector3Int location in position.allPositionsWithin) + { + Vector3Int local = location - position.min; + BrushCell cell = m_Cells[GetCellIndexWrapAround(local.x, local.y, local.z)]; + if (cell.tile == null) + continue; + + map.SetTransformMatrix(location, cell.matrix); + map.SetColor(location, cell.color); } } @@ -97,9 +128,15 @@ public override void BoxErase(GridLayout gridLayout, GameObject brushTarget, Bou return; Tilemap map = brushTarget.GetComponent(); + if (map == null) + return; + + var emptyTiles = new TileBase[position.size.x * position.size.y * position.size.z]; + map.SetTilesBlock(position, emptyTiles); foreach (Vector3Int location in position.allPositionsWithin) { - EraseCell(location, map); + map.SetTransformMatrix(location, Matrix4x4.identity); + map.SetColor(location, Color.white); } } @@ -112,10 +149,10 @@ public override void FloodFill(GridLayout gridLayout, GameObject brushTarget, Ve return; Tilemap map = brushTarget.GetComponent(); - if (map != null) - { - map.FloodFill(position, cells[0].tile); - } + if (map == null) + return; + + map.FloodFill(position, cells[0].tile); } public override void Rotate(RotationDirection direction, Grid.CellLayout layout) @@ -251,12 +288,12 @@ public override void Pick(GridLayout gridLayout, GameObject brushTarget, BoundsI private void PickCell(Vector3Int position, Vector3Int brushPosition, Tilemap tilemap) { - if (tilemap != null) - { - SetTile(brushPosition, tilemap.GetTile(position)); - SetMatrix(brushPosition, tilemap.GetTransformMatrix(position)); - SetColor(brushPosition, tilemap.GetColor(position)); - } + if (tilemap == null) + return; + + SetTile(brushPosition, tilemap.GetTile(position)); + SetMatrix(brushPosition, tilemap.GetTransformMatrix(position)); + SetColor(brushPosition, tilemap.GetColor(position)); } public override void MoveStart(GridLayout gridLayout, GameObject brushTarget, BoundsInt position) @@ -265,14 +302,14 @@ public override void MoveStart(GridLayout gridLayout, GameObject brushTarget, Bo UpdateSizeAndPivot(new Vector3Int(position.size.x, position.size.y, 1), Vector3Int.zero); Tilemap tilemap = brushTarget.GetComponent(); - if (tilemap != null) + if (tilemap == null) + return; + + foreach (Vector3Int pos in position.allPositionsWithin) { - foreach (Vector3Int pos in position.allPositionsWithin) - { - Vector3Int brushPosition = new Vector3Int(pos.x - position.x, pos.y - position.y, 0); - PickCell(pos, brushPosition, tilemap); - tilemap.SetTile(pos, null); - } + Vector3Int brushPosition = new Vector3Int(pos.x - position.x, pos.y - position.y, 0); + PickCell(pos, brushPosition, tilemap); + tilemap.SetTile(pos, null); } } @@ -402,26 +439,6 @@ private void SizeUpdated() } } - private static void SetTilemapCell(Tilemap map, Vector3Int location, TileBase tile, Matrix4x4 transformMatrix, Color color) - { - if (map == null) - return; - - map.SetTile(location, tile); - map.SetTransformMatrix(location, transformMatrix); - map.SetColor(location, color); - } - - private static void ClearTilemapCell(Tilemap map, Vector3Int location) - { - if (map == null) - return; - - map.SetTile(location, null); - map.SetTransformMatrix(location, Matrix4x4.identity); - map.SetColor(location, Color.white); - } - public override int GetHashCode() { int hash = 0; @@ -453,6 +470,7 @@ public override int GetHashCode() { hash = tile != null ? tile.GetInstanceID() : 0; hash = hash * 33 + matrix.GetHashCode(); + hash = hash * 33 + matrix.rotation.GetHashCode(); hash = hash * 33 + color.GetHashCode(); } return hash; diff --git a/Modules/TilemapEditor/Editor/Managed/Grid/GridBrushEditor.cs b/Modules/TilemapEditor/Editor/Managed/Grid/GridBrushEditor.cs index 52f2e82669..18c19f5850 100644 --- a/Modules/TilemapEditor/Editor/Managed/Grid/GridBrushEditor.cs +++ b/Modules/TilemapEditor/Editor/Managed/Grid/GridBrushEditor.cs @@ -228,11 +228,6 @@ public override void RegisterUndo(GameObject brushTarget, GridBrushBase.Tool too { if (brushTarget != null) { - var tilemap = brushTarget.GetComponent(); - if (tilemap != null) - { - Undo.RecordObject(tilemap, tool.ToString()); - } Undo.RegisterFullObjectHierarchyUndo(brushTarget, tool.ToString()); } } diff --git a/Modules/TilemapEditor/Editor/Managed/Grid/GridBrushEditorBase.cs b/Modules/TilemapEditor/Editor/Managed/Grid/GridBrushEditorBase.cs index 80d520831b..72ceacb7d1 100644 --- a/Modules/TilemapEditor/Editor/Managed/Grid/GridBrushEditorBase.cs +++ b/Modules/TilemapEditor/Editor/Managed/Grid/GridBrushEditorBase.cs @@ -56,6 +56,12 @@ internal static void OnPaintSceneGUIInternal(GridLayout gridLayout, GameObject b } GridEditorUtility.DrawGridMarquee(gridLayout, position, color); + if (position.zMin != 0) + { + var zeroBounds = position; + zeroBounds.zMin = 0; + GridEditorUtility.DrawGridMarquee(gridLayout, zeroBounds, Color.blue); + } } } } diff --git a/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintPaletteClipboard.cs b/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintPaletteClipboard.cs index 74278db16c..fcdb778094 100644 --- a/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintPaletteClipboard.cs +++ b/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintPaletteClipboard.cs @@ -353,7 +353,7 @@ protected override void OnDisable() public override void OnGUI() { - if (guiRect.width == 0f || guiRect.height == 0f) + if (Mathf.Approximately(guiRect.width, 0f) || Mathf.Approximately(guiRect.height, 0f)) return; UpdateMouseGridPosition(); @@ -389,7 +389,7 @@ public override void OnGUI() public void OnViewSizeChanged(Rect oldSize, Rect newSize) { - if (oldSize.height * oldSize.width * newSize.height * newSize.width == 0f) + if (Mathf.Approximately(oldSize.height * oldSize.width * newSize.height * newSize.width, 0f)) return; Camera cam = previewUtility.camera; @@ -625,13 +625,13 @@ private void RenderDragAndDropPreview() RectInt rect = TileDragAndDrop.GetMinMaxRect(m_HoverData.Keys.ToList()); rect.position += mouseGridPosition; DragAndDrop.visualMode = DragAndDropVisualMode.Copy; - GridEditorUtility.DrawGridMarquee(grid, new BoundsInt(new Vector3Int(rect.xMin, rect.yMin, 0), new Vector3Int(rect.width, rect.height, 1)), Color.white); + GridEditorUtility.DrawGridMarquee(grid, new BoundsInt(new Vector3Int(rect.xMin, rect.yMin, zPosition), new Vector3Int(rect.width, rect.height, 1)), Color.white); } private void RenderGrid() { // MeshTopology.Lines doesn't give nice pixel perfect grid so we have to have separate codepath with MeshTopology.Quads specially for palette window here - if (m_GridMesh == null && grid.cellLayout == Grid.CellLayout.Rectangle) + if (m_GridMesh == null && grid.cellLayout == GridLayout.CellLayout.Rectangle) m_GridMesh = GridEditorUtility.GenerateCachedGridMesh(grid, k_GridColor, 1f / LocalToScreenRatio(), paddedBoundsInt, MeshTopology.Quads); GridEditorUtility.DrawGridGizmo(grid, grid.transform, k_GridColor, ref m_GridMesh, ref m_GridMaterial); @@ -750,7 +750,7 @@ public void SetEditorPreviewTile(Tilemap tilemap, Vector2Int position, TileBase public void SetTile(Tilemap tilemap, Vector2Int position, TileBase tile, Color color, Matrix4x4 matrix) { - Vector3Int pos3 = new Vector3Int(position.x, position.y, 0); + Vector3Int pos3 = new Vector3Int(position.x, position.y, zPosition); tilemap.SetTile(pos3, tile); tilemap.SetColor(pos3, color); tilemap.SetTransformMatrix(pos3, matrix); @@ -809,7 +809,7 @@ protected override void PickBrush(BoundsInt position, Vector3Int pickingStart) gridBrush.Pick(grid, brushTarget, position, pickingStart); if (!PaintableGrid.InGridEditMode()) - EditMode.ChangeEditMode(EditMode.SceneViewEditMode.GridPainting, new Bounds(), GridPaintingState.instance); + EditMode.ChangeEditMode(EditMode.SceneViewEditMode.GridPainting, GridPaintingState.instance); m_ActivePick = new RectInt(position.min.x, position.min.y, position.size.x, position.size.y); } @@ -873,7 +873,7 @@ private void PingTileAsset(RectInt rect) // Only able to ping asset if only one asset is selected if (rect.size == Vector2Int.zero && tilemap != null) { - TileBase tile = tilemap.GetTile(new Vector3Int(rect.xMin, rect.yMin, 0)); + TileBase tile = tilemap.GetTile(new Vector3Int(rect.xMin, rect.yMin, zPosition)); EditorGUIUtility.PingObject(tile); Selection.activeObject = tile; } @@ -931,29 +931,6 @@ protected void DrawSelectionGizmo(RectInt rect) private void HandleMouseEnterLeave() { - if (Event.current.type == EventType.MouseEnterWindow) - { - if (PaintableGrid.InGridEditMode()) - { - GridPaintingState.activeGrid = this; - Event.current.Use(); - } - } - else if (Event.current.type == EventType.MouseLeaveWindow) - { - if (m_PreviousMousePosition.HasValue && guiRect.Contains(m_PreviousMousePosition.Value) && GridPaintingState.activeBrushEditor != null) - { - GridPaintingState.activeBrushEditor.OnMouseLeave(); - } - m_PreviousMousePosition = null; - if (PaintableGrid.InGridEditMode()) - { - GridPaintingState.activeGrid = null; - Event.current.Use(); - Repaint(); - } - } - if (guiRect.Contains(Event.current.mousePosition)) { if (m_PreviousMousePosition.HasValue && !guiRect.Contains(m_PreviousMousePosition.Value) || !m_PreviousMousePosition.HasValue) @@ -998,7 +975,7 @@ private void CallOnPaintSceneGUI(Vector2Int position) rect = new RectInt(GridSelection.position.xMin, GridSelection.position.yMin, GridSelection.position.size.x, GridSelection.position.size.y); var gridLayout = tilemap != null ? tilemap as GridLayout : grid as GridLayout; - BoundsInt brushBounds = new BoundsInt(new Vector3Int(rect.x, rect.y, 0), new Vector3Int(rect.width, rect.height, 1)); + BoundsInt brushBounds = new BoundsInt(new Vector3Int(rect.x, rect.y, zPosition), new Vector3Int(rect.width, rect.height, 1)); if (GridPaintingState.activeBrushEditor != null) GridPaintingState.activeBrushEditor.OnPaintSceneGUI(gridLayout, brushTarget, brushBounds, EditModeToBrushTool(EditMode.editMode), m_MarqueeStart.HasValue || executing); diff --git a/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintPaletteWindow.cs b/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintPaletteWindow.cs index 04dc841fa8..17081082fc 100644 --- a/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintPaletteWindow.cs +++ b/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintPaletteWindow.cs @@ -97,6 +97,8 @@ static class Styles public static readonly GUIContent tilePalette = EditorGUIUtility.TrTextContent("Tile Palette"); public static readonly GUIContent edit = EditorGUIUtility.TrTextContent("Edit"); public static readonly GUIContent editModified = EditorGUIUtility.TrTextContent("Edit*"); + public static readonly GUIContent zPosition = EditorGUIUtility.TrTextContent("Z Position"); + public static readonly GUIContent resetZPosition = EditorGUIUtility.TrTextContent("Reset"); public static readonly GUIStyle ToolbarTitleStyle = "Toolbar"; public static readonly GUIStyle dragHandle = "RL DragHandle"; public static readonly float dragPadding = 3f; @@ -280,6 +282,31 @@ static void FlipBrushY() FlipBrush(GridBrush.FlipAxis.Y); } + static void ChangeBrushZ(int change) + { + GridPaintingState.gridBrush.ChangeZPosition(change); + GridPaintingState.activeGrid.ChangeZPosition(change); + GridPaintingState.activeGrid.Repaint(); + foreach (var window in GridPaintPaletteWindow.instances) + { + window.Repaint(); + } + } + + [Shortcut("Grid Painting/Increase Z", typeof(ShortcutContext), "-")] + static void IncreaseBrushZ() + { + if (GridPaintingState.gridBrush != null && GridPaintingState.activeGrid != null) + ChangeBrushZ(1); + } + + [Shortcut("Grid Painting/Decrease Z", typeof(ShortcutContext), "=")] + static void DecreaseBrushZ() + { + if (GridPaintingState.gridBrush != null && GridPaintingState.activeGrid != null) + ChangeBrushZ(-1); + } + [SettingsProvider] internal static SettingsProvider CreateSettingsProvider() { @@ -556,7 +583,9 @@ public void SavePalette() GridPaintingState.savingPalette = true; SetHideFlagsRecursivelyIgnoringTilemapChildren(paletteInstance, HideFlags.HideInHierarchy); string path = AssetDatabase.GetAssetPath(palette); + #pragma warning disable CS0618 // Type or member is obsolete PrefabUtility.ReplacePrefabAssetNameBased(paletteInstance, path, true); + #pragma warning restore CS0618 // Type or member is obsolete SetHideFlagsRecursivelyIgnoringTilemapChildren(paletteInstance, HideFlags.HideAndDontSave); GridPaintingState.savingPalette = false; } @@ -688,6 +717,7 @@ public void OnEnable() GridPaintingState.brushChanged += OnBrushChanged; SceneView.onSceneGUIDelegate += OnSceneViewGUI; PrefabUtility.prefabInstanceUpdated += PrefabInstanceUpdated; + EditorApplication.projectWasLoaded += OnProjectLoaded; AssetPreview.SetPreviewTextureCacheSize(256, GetInstanceID()); wantsMouseMove = true; @@ -726,6 +756,12 @@ private void PrefabInstanceUpdated(GameObject updatedPrefab) } } + private void OnProjectLoaded() + { + // ShortcutIntegration instance is recreated after LoadLayout which wipes the OnEnable registration + ShortcutIntegration.instance.contextManager.RegisterToolContext(m_ShortcutContext); + } + private void OnBrushChanged(GridBrushBase brush) { DisableFocus(); @@ -774,6 +810,7 @@ public void OnDisable() GridPaintingState.brushChanged -= OnBrushChanged; GridPaintingState.UnregisterPainterInterest(this); PrefabUtility.prefabInstanceUpdated -= PrefabInstanceUpdated; + EditorApplication.projectWasLoaded -= OnProjectLoaded; ShortcutIntegration.instance.contextManager.DeregisterToolContext(m_ShortcutContext); } @@ -787,7 +824,7 @@ private void OnScenePaintTargetChanged(GameObject scenePaintTarget) public void ChangeToTool(GridBrushBase.Tool tool) { - EditMode.ChangeEditMode(PaintableGrid.BrushToolToEditMode(tool), new Bounds(Vector3.zero, Vector3.positiveInfinity), GridPaintingState.instance); + EditMode.ChangeEditMode(PaintableGrid.BrushToolToEditMode(tool), GridPaintingState.instance); Repaint(); } @@ -838,12 +875,18 @@ private void CallOnToolDeactivated() } } + internal void ResetZPosition() + { + GridPaintingState.gridBrush.ResetZPosition(); + GridPaintingState.lastActiveGrid.ResetZPosition(); + } + private void OnBrushInspectorGUI() { - var brush = GridPaintingState.gridBrush; - if (brush == null) + if (GridPaintingState.gridBrush == null) return; + // Brush Inspector GUI EditorGUI.BeginChangeCheck(); if (GridPaintingState.activeBrushEditor != null) GridPaintingState.activeBrushEditor.OnPaintInspectorGUI(); @@ -853,6 +896,25 @@ private void OnBrushInspectorGUI() { GridPaletteBrushes.ActiveGridBrushAssetChanged(); } + + // Z Position Inspector + var hasLastActiveGrid = GridPaintingState.lastActiveGrid != null; + using (new EditorGUI.DisabledScope(!hasLastActiveGrid)) + { + EditorGUILayout.BeginHorizontal(); + EditorGUI.BeginChangeCheck(); + var zPosition = EditorGUILayout.DelayedIntField(Styles.zPosition, hasLastActiveGrid ? GridPaintingState.lastActiveGrid.zPosition : 0); + if (EditorGUI.EndChangeCheck()) + { + GridPaintingState.gridBrush.ChangeZPosition(zPosition - GridPaintingState.lastActiveGrid.zPosition); + GridPaintingState.lastActiveGrid.zPosition = zPosition; + } + if (GUILayout.Button(Styles.resetZPosition)) + { + ResetZPosition(); + } + EditorGUILayout.EndHorizontal(); + } } private bool IsObjectPrefabInstance(Object target) @@ -938,19 +1000,19 @@ private void SelectTarget(int i, object o) var option = EditorUtility.DisplayDialogComplex(TilePaletteProperties.targetEditModeDialogTitle , TilePaletteProperties.targetEditModeDialogMessage , TilePaletteProperties.targetEditModeDialogYes - , TilePaletteProperties.targetEditModeDialogChange - , TilePaletteProperties.targetEditModeDialogNo); + , TilePaletteProperties.targetEditModeDialogNo + , TilePaletteProperties.targetEditModeDialogChange); switch (option) { case 0: GoToPrefabMode(obj); return; case 1: - var settingsWindow = SettingsWindow.Show(SettingsScopes.User); - settingsWindow.FilterProviders(TilePaletteProperties.targetEditModeLookup); + // Do nothing here for "No" break; case 2: - // Do nothing here for "No" + var settingsWindow = SettingsWindow.Show(SettingsScopes.User); + settingsWindow.FilterProviders(TilePaletteProperties.targetEditModeLookup); break; } } @@ -1037,7 +1099,7 @@ private void OpenAddPalettePopup(Rect rect) private void OnClipboardGUI(Rect position) { - if (Event.current.type != EventType.Layout && position.Contains(Event.current.mousePosition) && GridPaintingState.activeGrid != clipboardView) + if (Event.current.type != EventType.Layout && position.Contains(Event.current.mousePosition) && GridPaintingState.activeGrid != clipboardView && clipboardView.unlocked) { GridPaintingState.activeGrid = clipboardView; SceneView.RepaintAll(); @@ -1182,7 +1244,18 @@ private static void OnPostprocessAllAssets(string[] importedAssets, string[] del public class PaletteAssetModificationProcessor : AssetModificationProcessor { + static void OnWillCreateAsset(string assetName) + { + SavePalettesIfRequired(); + } + static string[] OnWillSaveAssets(string[] paths) + { + SavePalettesIfRequired(); + return paths; + } + + static void SavePalettesIfRequired() { if (!GridPaintingState.savingPalette) { @@ -1195,7 +1268,6 @@ static string[] OnWillSaveAssets(string[] paths) } } } - return paths; } } } diff --git a/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintingState.cs b/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintingState.cs index 7f25f4a305..9010374679 100644 --- a/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintingState.cs +++ b/Modules/TilemapEditor/Editor/Managed/Grid/GridPaintingState.cs @@ -15,6 +15,7 @@ internal class GridPaintingState : ScriptableSingleton, ITool [SerializeField] private GameObject m_ScenePaintTarget; // Which GameObject in scene is considered as painting target [SerializeField] private GridBrushBase m_Brush; // Which brush will handle painting callbacks [SerializeField] private PaintableGrid m_ActiveGrid; // Grid that has painting focus (can be palette, too) + [SerializeField] private PaintableGrid m_LastActiveGrid; // Grid that last had painting focus (can be palette, too) [SerializeField] private HashSet m_InterestedPainters = new HashSet(); // A list of objects that can paint using the GridPaintingState private GameObject[] m_CachedPaintTargets = null; @@ -145,7 +146,17 @@ public static Editor fallbackEditor public static PaintableGrid activeGrid { get { return instance.m_ActiveGrid; } - set { instance.m_ActiveGrid = value; } + set + { + instance.m_ActiveGrid = value; + if (instance.m_ActiveGrid != null) + instance.m_LastActiveGrid = value; + } + } + + public static PaintableGrid lastActiveGrid + { + get { return instance.m_LastActiveGrid; } } public static bool ValidatePaintTarget(GameObject candidate) diff --git a/Modules/TilemapEditor/Editor/Managed/Grid/GridPaletteAddPopup.cs b/Modules/TilemapEditor/Editor/Managed/Grid/GridPaletteAddPopup.cs index 952df16b08..6509d90f1d 100644 --- a/Modules/TilemapEditor/Editor/Managed/Grid/GridPaletteAddPopup.cs +++ b/Modules/TilemapEditor/Editor/Managed/Grid/GridPaletteAddPopup.cs @@ -104,6 +104,9 @@ internal void OnGUI() { if (GUILayout.Button(Styles.ok)) { + // case 1077362: Close window to prevent overlap with OS folder window when saving new palette asset + Close(); + var swizzle = Grid.CellSwizzle.XYZ; if (m_Layout == GridLayout.CellLayout.Hexagon) swizzle = Styles.hexagonSwizzleTypeValue[m_HexagonLayout]; @@ -114,6 +117,7 @@ internal void OnGUI() m_Owner.palette = go; m_Owner.Repaint(); } + GUIUtility.ExitGUI(); } } diff --git a/Modules/TilemapEditor/Editor/Managed/Grid/PaintableGrid.cs b/Modules/TilemapEditor/Editor/Managed/Grid/PaintableGrid.cs index b1dced7da9..72dfeac3c6 100644 --- a/Modules/TilemapEditor/Editor/Managed/Grid/PaintableGrid.cs +++ b/Modules/TilemapEditor/Editor/Managed/Grid/PaintableGrid.cs @@ -53,11 +53,13 @@ protected virtual void OnBrushPickCancelled() {} private MarqueeType m_MarqueeType = MarqueeType.None; private bool m_IsExecuting; private EditMode.SceneViewEditMode m_ModeBeforePicking; + private int m_ZPosition; public Vector2Int mouseGridPosition { get { return m_MouseGridPosition; } } public bool isPicking { get { return m_MarqueeType == MarqueeType.Pick; } } public bool isBoxing { get { return m_MarqueeType == MarqueeType.Box; } } public Grid.CellLayout cellLayout { get { return CellLayout(); } } + public int zPosition { get { return m_ZPosition; } set { m_ZPosition = value; } } protected bool executing { get { return m_IsExecuting; } set { m_IsExecuting = value && isHotControl; } } @@ -101,12 +103,20 @@ public virtual void OnGUI() } } - protected void UpdateMouseGridPosition() + protected void ResetPreviousMousePositionToCurrentPosition() { - if (Event.current.type == EventType.MouseDrag || Event.current.type == EventType.MouseMove || Event.current.type == EventType.DragUpdated) - { - m_MouseGridPositionChanged = false; + m_PreviousMouseGridPosition = m_MouseGridPosition; + } + protected void UpdateMouseGridPosition(bool forceUpdate = false) + { + if (Event.current.type == EventType.MouseDrag + || Event.current.type == EventType.MouseMove + // Case 1075857: Mouse Down when window is not in focus needs to update mouse grid position + || Event.current.type == EventType.MouseDown + || Event.current.type == EventType.DragUpdated + || forceUpdate) + { Vector2Int newGridPosition = ScreenToGrid(Event.current.mousePosition); if (newGridPosition != m_MouseGridPosition) { @@ -116,14 +126,23 @@ protected void UpdateMouseGridPosition() newGridPosition.x = m_MouseGridPosition.x + Math.Sign(delta.x) * k_MaxMouseCellDelta; if (Mathf.Abs(delta.y) > k_MaxMouseCellDelta) newGridPosition.y = m_MouseGridPosition.y + Math.Sign(delta.y) * k_MaxMouseCellDelta; - m_PreviousMouseGridPosition = m_MouseGridPosition; + ResetPreviousMousePositionToCurrentPosition(); m_MouseGridPosition = newGridPosition; - m_MouseGridPositionChanged = true; - m_PositionChangeRepaintDone = false; + MouseGridPositionChanged(); + } + else if (!forceUpdate) + { + m_MouseGridPositionChanged = false; } } } + private void MouseGridPositionChanged() + { + m_MouseGridPositionChanged = true; + m_PositionChangeRepaintDone = false; + } + private void HandleEditModeChange() { // Handles changes in EditMode while tool is expected to be in the same mode @@ -178,7 +197,7 @@ private void HandleBrushPicking() if (evt.type == EventType.MouseDrag && isHotControl && m_MarqueeStart.HasValue && m_MarqueeType == MarqueeType.Pick && IsPickingEvent(evt)) { RectInt rect = GridEditorUtility.GetMarqueeRect(m_MarqueeStart.Value, mouseGridPosition); - OnBrushPickDragged(new BoundsInt(new Vector3Int(rect.xMin, rect.yMin, 0), new Vector3Int(rect.size.x, rect.size.y, 1))); + OnBrushPickDragged(new BoundsInt(new Vector3Int(rect.xMin, rect.yMin, zPosition), new Vector3Int(rect.size.x, rect.size.y, 1))); Event.current.Use(); GUI.changed = true; } @@ -189,7 +208,7 @@ private void HandleBrushPicking() { RectInt rect = GridEditorUtility.GetMarqueeRect(m_MarqueeStart.Value, mouseGridPosition); Vector2Int pivot = GetMarqueePivot(m_MarqueeStart.Value, mouseGridPosition); - PickBrush(new BoundsInt(new Vector3Int(rect.xMin, rect.yMin, 0), new Vector3Int(rect.size.x, rect.size.y, 1)), new Vector3Int(pivot.x, pivot.y, 0)); + PickBrush(new BoundsInt(new Vector3Int(rect.xMin, rect.yMin, zPosition), new Vector3Int(rect.size.x, rect.size.y, 1)), new Vector3Int(pivot.x, pivot.y, 0)); if (inEditMode && EditMode.editMode != m_ModeBeforePicking) { @@ -245,7 +264,7 @@ private void HandleSelectTool() if (evt.type == EventType.MouseUp && m_MarqueeType == MarqueeType.Select) { RectInt rect = GridEditorUtility.GetMarqueeRect(m_MarqueeStart.Value, mouseGridPosition); - Select(new BoundsInt(new Vector3Int(rect.xMin, rect.yMin, 0), new Vector3Int(rect.size.x, rect.size.y, 1))); + Select(new BoundsInt(new Vector3Int(rect.xMin, rect.yMin, zPosition), new Vector3Int(rect.size.x, rect.size.y, 1))); Event.current.Use(); } if (evt.control) @@ -288,7 +307,7 @@ private void HandleMoveTool() { executing = true; BoundsInt previousRect = GridSelection.position; - BoundsInt previousBounds = new BoundsInt(new Vector3Int(previousRect.xMin, previousRect.yMin, 0), new Vector3Int(previousRect.size.x, previousRect.size.y, 1)); + BoundsInt previousBounds = new BoundsInt(new Vector3Int(previousRect.xMin, previousRect.yMin, GridSelection.position.zMin), new Vector3Int(previousRect.size.x, previousRect.size.y, 1)); Vector2Int direction = mouseGridPosition - m_PreviousMove.Value; BoundsInt pos = GridSelection.position; @@ -326,13 +345,13 @@ private void HandleBrushPaintAndErase() { if (EditMode.editMode != EditMode.SceneViewEditMode.GridEraser) EditMode.ChangeEditMode(EditMode.SceneViewEditMode.GridEraser, GridPaintingState.instance); - Erase(new Vector3Int(mouseGridPosition.x, mouseGridPosition.y, 0)); + Erase(new Vector3Int(mouseGridPosition.x, mouseGridPosition.y, zPosition)); } else { if (EditMode.editMode != EditMode.SceneViewEditMode.GridPainting) EditMode.ChangeEditMode(EditMode.SceneViewEditMode.GridPainting, GridPaintingState.instance); - Paint(new Vector3Int(mouseGridPosition.x, mouseGridPosition.y, 0)); + Paint(new Vector3Int(mouseGridPosition.x, mouseGridPosition.y, zPosition)); } Event.current.Use(); @@ -350,9 +369,9 @@ private void HandleBrushPaintAndErase() for (int i = 1; i < points.Count; i++) { if (IsErasingEvent(evt)) - Erase(new Vector3Int(points[i].x, points[i].y, 0)); + Erase(new Vector3Int(points[i].x, points[i].y, zPosition)); else - Paint(new Vector3Int(points[i].x, points[i].y, 0)); + Paint(new Vector3Int(points[i].x, points[i].y, zPosition)); } Event.current.Use(); GUI.changed = true; @@ -405,7 +424,7 @@ private void HandleFloodFill() { executing = false; RegisterUndo(); - FloodFill(new Vector3Int(mouseGridPosition.x, mouseGridPosition.y, 0)); + FloodFill(new Vector3Int(mouseGridPosition.x, mouseGridPosition.y, zPosition)); GUI.changed = true; Event.current.Use(); GUIUtility.hotControl = 0; @@ -442,9 +461,9 @@ private void HandleBoxTool() RegisterUndo(); RectInt rect = GridEditorUtility.GetMarqueeRect(m_MarqueeStart.Value, mouseGridPosition); if (evt.shift) - BoxErase(new BoundsInt(rect.x, rect.y, 0, rect.size.x, rect.size.y, 1)); + BoxErase(new BoundsInt(rect.x, rect.y, zPosition, rect.size.x, rect.size.y, 1)); else - BoxFill(new BoundsInt(rect.x, rect.y, 0, rect.size.x, rect.size.y, 1)); + BoxFill(new BoundsInt(rect.x, rect.y, zPosition, rect.size.x, rect.size.y, 1)); Event.current.Use(); executing = false; GUI.changed = true; @@ -464,6 +483,23 @@ private Vector2Int GetMarqueePivot(Vector2Int start, Vector2Int end) return pivot; } + public void ChangeZPosition(int change) + { + m_ZPosition += change; + MouseGridPositionChanged(); + Repaint(); + } + + public void ResetZPosition() + { + if (m_ZPosition == 0) + return; + + m_ZPosition = 0; + MouseGridPositionChanged(); + Repaint(); + } + public static bool InGridEditMode() { return diff --git a/Modules/TilemapEditor/Editor/Managed/Grid/PaintableSceneViewGrid.cs b/Modules/TilemapEditor/Editor/Managed/Grid/PaintableSceneViewGrid.cs index f8f53f27df..a538cc4e38 100644 --- a/Modules/TilemapEditor/Editor/Managed/Grid/PaintableSceneViewGrid.cs +++ b/Modules/TilemapEditor/Editor/Managed/Grid/PaintableSceneViewGrid.cs @@ -18,6 +18,7 @@ internal class PaintableSceneViewGrid : PaintableGrid private Grid grid { get { return brushTarget != null ? brushTarget.GetComponentInParent() : (Selection.activeGameObject != null ? Selection.activeGameObject.GetComponentInParent() : null); } } private GridBrushBase gridBrush { get { return GridPaintingState.gridBrush; } } private SceneView activeSceneView = null; + private int sceneViewTransformHash; GameObject brushTarget { @@ -64,7 +65,12 @@ private Rect GetSceneViewPositionRect(SceneView sceneView) public void OnSceneGUI(SceneView sceneView) { - UpdateMouseGridPosition(); + HandleMouseEnterLeave(sceneView); + + // Case 1077400: SceneView camera transform changes may update the mouse grid position even though the mouse position has not changed + var currentSceneViewTransformHash = sceneView.camera.transform.localToWorldMatrix.GetHashCode(); + UpdateMouseGridPosition(currentSceneViewTransformHash == sceneViewTransformHash); + sceneViewTransformHash = currentSceneViewTransformHash; var dot = 1.0f; var gridView = GetGridView(); @@ -88,7 +94,6 @@ public void OnSceneGUI(SceneView sceneView) EditorGUIUtility.AddCursorRect(GetSceneViewPositionRect(sceneView), MouseCursor.CustomCursor); } } - HandleMouseEnterLeave(sceneView); } private void HandleMouseEnterLeave(SceneView sceneView) @@ -132,6 +137,7 @@ private void OnMouseEnter(SceneView sceneView) GridPaintingState.activeBrushEditor.OnMouseEnter(); GridPaintingState.activeGrid = this; activeSceneView = sceneView; + ResetPreviousMousePositionToCurrentPosition(); } private void OnMouseLeave(SceneView sceneView) @@ -320,16 +326,16 @@ void CallOnPaintSceneGUI() rect = new RectInt(GridSelection.position.xMin, GridSelection.position.yMin, GridSelection.position.size.x, GridSelection.position.size.y); var layoutGrid = tilemap != null ? tilemap as GridLayout : grid as GridLayout; - + BoundsInt brushBounds = new BoundsInt(new Vector3Int(rect.x, rect.y, zPosition), new Vector3Int(rect.width, rect.height, 1)); if (GridPaintingState.activeBrushEditor != null) { - GridPaintingState.activeBrushEditor.OnPaintSceneGUI(layoutGrid, brushTarget, - new BoundsInt(new Vector3Int(rect.x, rect.y, 0), new Vector3Int(rect.width, rect.height, 1)), - EditModeToBrushTool(EditMode.editMode), m_MarqueeStart.HasValue || executing); + GridPaintingState.activeBrushEditor.OnPaintSceneGUI(layoutGrid, brushTarget, brushBounds + , EditModeToBrushTool(EditMode.editMode), m_MarqueeStart.HasValue || executing); } else // Fallback when user hasn't defined custom editor { - GridBrushEditorBase.OnPaintSceneGUIInternal(layoutGrid, brushTarget, new BoundsInt(new Vector3Int(rect.x, rect.y, 0), new Vector3Int(rect.width, rect.height, 1)), EditModeToBrushTool(EditMode.editMode), m_MarqueeStart.HasValue || executing); + GridBrushEditorBase.OnPaintSceneGUIInternal(layoutGrid, brushTarget, brushBounds + , EditModeToBrushTool(EditMode.editMode), m_MarqueeStart.HasValue || executing); } } diff --git a/Modules/TilemapEditor/Editor/Managed/TilemapEditor.cs b/Modules/TilemapEditor/Editor/Managed/TilemapEditor.cs index 36b436c627..292b2177bc 100644 --- a/Modules/TilemapEditor/Editor/Managed/TilemapEditor.cs +++ b/Modules/TilemapEditor/Editor/Managed/TilemapEditor.cs @@ -30,8 +30,10 @@ private static class Styles public static readonly GUIContent tilemapColorLabel = EditorGUIUtility.TrTextContent("Color", "Color tinting all Sprites from tiles in the tilemap"); public static readonly GUIContent tileAnchorLabel = EditorGUIUtility.TrTextContent("Tile Anchor", "Anchoring position for Sprites from tiles in the tilemap"); public static readonly GUIContent orientationLabel = EditorGUIUtility.TrTextContent("Orientation", "Orientation for tiles in the tilemap"); - public static readonly GUIContent pointTopHexagonCreateUndo = EditorGUIUtility.TrTextContent("Hexagonal Point Top Tilemap"); - public static readonly GUIContent flatTopHexagonCreateUndo = EditorGUIUtility.TrTextContent("Hexagonal Flat Top Tilemap"); + public static readonly string pointTopHexagonCreateUndo = L10n.Tr("Hexagonal Point Top Tilemap"); + public static readonly string flatTopHexagonCreateUndo = L10n.Tr("Hexagonal Flat Top Tilemap"); + public static readonly string isometricCreateUndo = L10n.Tr("Isometric Tilemap"); + public static readonly string isometricZAsYCreateUndo = L10n.Tr("Isometric Z As Y Tilemap"); } private void OnEnable() @@ -89,17 +91,28 @@ internal static void CreateRectangularTilemap() [MenuItem("GameObject/2D Object/Hexagonal Point Top Tilemap")] internal static void CreateHexagonalPointTopTilemap() { - CreateHexagonalTilemap(GridLayout.CellSwizzle.XYZ, Styles.pointTopHexagonCreateUndo.text); + CreateHexagonalTilemap(GridLayout.CellSwizzle.XYZ, Styles.pointTopHexagonCreateUndo); } [MenuItem("GameObject/2D Object/Hexagonal Flat Top Tilemap")] internal static void CreateHexagonalFlatTopTilemap() { - CreateHexagonalTilemap(GridLayout.CellSwizzle.YXZ, Styles.flatTopHexagonCreateUndo.text); + CreateHexagonalTilemap(GridLayout.CellSwizzle.YXZ, Styles.flatTopHexagonCreateUndo); } [MenuItem("GameObject/2D Object/Isometric Tilemap")] internal static void CreateIsometricTilemap() + { + CreateIsometricTilemap(GridLayout.CellLayout.Isometric, Styles.isometricCreateUndo); + } + + [MenuItem("GameObject/2D Object/Isometric Z As Y Tilemap")] + internal static void CreateIsometricZAsYTilemap() + { + CreateIsometricTilemap(GridLayout.CellLayout.IsometricZAsY, Styles.isometricZAsYCreateUndo); + } + + private static void CreateIsometricTilemap(GridLayout.CellLayout isometricLayout, string undoMessage) { var root = FindOrCreateRootGrid(); var uniqueName = GameObjectUtility.GetUniqueNameForSibling(root.transform, "Tilemap"); @@ -108,12 +121,16 @@ internal static void CreateIsometricTilemap() tilemapGO.transform.position = Vector3.zero; var grid = root.GetComponent(); - grid.cellLayout = Grid.CellLayout.Isometric; - grid.cellSize = new Vector3(1.0f, 0.5f, 0.0f); + // Case 1071703: Do not reset cell size if adding a new Tilemap to an existing Grid of the same layout + if (isometricLayout != grid.cellLayout) + { + grid.cellLayout = isometricLayout; + grid.cellSize = new Vector3(1.0f, 0.5f, 0.0f); + } var tilemapRenderer = tilemapGO.GetComponent(); tilemapRenderer.sortOrder = TilemapRenderer.SortOrder.TopRight; - Undo.RegisterCreatedObjectUndo(tilemapGO, "Create Isometric Tilemap"); + Undo.RegisterCreatedObjectUndo(tilemapGO, undoMessage); } private static void CreateHexagonalTilemap(GridLayout.CellSwizzle swizzle, string undoMessage) diff --git a/Modules/TreeEditor/TreeEditor.cs b/Modules/TreeEditor/TreeEditor.cs index 23a43f22f9..8ee5d00614 100644 --- a/Modules/TreeEditor/TreeEditor.cs +++ b/Modules/TreeEditor/TreeEditor.cs @@ -310,8 +310,6 @@ static void CreateNewTree(MenuCommand menuCommand) AssetDatabase.AddObjectToAsset(materialCutoutAsset, prefabAsset); AssetDatabase.AddObjectToAsset(data, prefabAsset); - PrefabUtility.ApplyPrefabInstance(prefabInstance); - GameObjectUtility.SetParentAndAlign(prefabInstance, menuCommand.context as GameObject); // Store Creation undo diff --git a/Modules/UIElements/ImmediateStylePainter.cs b/Modules/UIElements/ImmediateStylePainter.cs index 56e15fffdf..e6b037476e 100644 --- a/Modules/UIElements/ImmediateStylePainter.cs +++ b/Modules/UIElements/ImmediateStylePainter.cs @@ -119,18 +119,18 @@ public void DrawBackground() { IStyle style = currentElement.style; - // The background color is embedded in the texture/background image - if (style.backgroundImage.value != null) + if (style.backgroundColor != Color.clear) { - var painterParams = TextureStylePainterParameters.GetDefault(currentElement); + var painterParams = RectStylePainterParameters.GetDefault(currentElement); painterParams.border.SetWidth(0.0f); - DrawTexture(painterParams); + DrawRect(painterParams); } - else if (style.backgroundColor != Color.clear) + + if (style.backgroundImage.value != null) { - var painterParams = RectStylePainterParameters.GetDefault(currentElement); + var painterParams = TextureStylePainterParameters.GetDefault(currentElement); painterParams.border.SetWidth(0.0f); - DrawRect(painterParams); + DrawTexture(painterParams); } } diff --git a/Modules/UIElements/ListView.cs b/Modules/UIElements/ListView.cs index 64f5e45f30..d037bf99da 100644 --- a/Modules/UIElements/ListView.cs +++ b/Modules/UIElements/ListView.cs @@ -237,6 +237,13 @@ private void OnClick(MouseDownEvent evt) return; var clickedIndex = (int)(evt.localMousePosition.y / itemHeight); + + // ignore clicks on empty space + if (clickedIndex > itemsSource.Count - 1) + { + return; + } + switch (evt.clickCount) { case 1: diff --git a/Modules/UIElements/Panel.cs b/Modules/UIElements/Panel.cs index f08c931e8c..aca4cf735c 100644 --- a/Modules/UIElements/Panel.cs +++ b/Modules/UIElements/Panel.cs @@ -367,7 +367,7 @@ internal static long DefaultTimeSinceStartupMs() return (long)(Time.realtimeSinceStartup * 1000.0f); } - VisualElement PickAll(VisualElement root, Vector2 point, List picked = null) + internal static VisualElement PickAll(VisualElement root, Vector2 point, List picked = null) { Profiler.BeginSample("Panel.PickAll"); var result = PerformPick(root, point, picked); @@ -375,7 +375,7 @@ VisualElement PickAll(VisualElement root, Vector2 point, List pic return result; } - private VisualElement PerformPick(VisualElement root, Vector2 point, List picked = null) + private static VisualElement PerformPick(VisualElement root, Vector2 point, List picked = null) { // do not pick invisible if (root.visible == false) diff --git a/Modules/UIElements/StylePainter.cs b/Modules/UIElements/StylePainter.cs index 29c4cc0fe4..0bca9f788a 100644 --- a/Modules/UIElements/StylePainter.cs +++ b/Modules/UIElements/StylePainter.cs @@ -114,9 +114,7 @@ public static TextureStylePainterParameters GetDefault(VisualElement ve) { rect = GUIUtility.AlignRectToDevice(ve.rect), uv = new Rect(0, 0, 1, 1), - // When the background color is not clear, we have to embed it into the texture... - // whereas if the color is clear, the white color is used... - color = (style.backgroundColor != Color.clear) ? (Color)style.backgroundColor : (Color)Color.white, + color = (Color)Color.white, texture = style.backgroundImage, scaleMode = style.backgroundScaleMode, sliceLeft = style.sliceLeft, diff --git a/Modules/UIElements/VisualElement.cs b/Modules/UIElements/VisualElement.cs index 43ab959327..ccd739578f 100644 --- a/Modules/UIElements/VisualElement.cs +++ b/Modules/UIElements/VisualElement.cs @@ -620,9 +620,25 @@ internal void SetPanel(BaseVisualElementPanel p) elements.Add(this); GatherAllChildren(elements); - foreach (var e in elements) + EventDispatcher.Gate? pDispatcherGate = null; + if (p?.dispatcher != null) { - e.ChangePanel(p); + pDispatcherGate = new EventDispatcher.Gate(p.dispatcher); + } + + EventDispatcher.Gate? panelDispatcherGate = null; + if (panel?.dispatcher != null && panel.dispatcher != p?.dispatcher) + { + panelDispatcherGate = new EventDispatcher.Gate(panel.dispatcher); + } + + using (pDispatcherGate) + using (panelDispatcherGate) + { + foreach (var e in elements) + { + e.ChangePanel(p); + } } } finally @@ -655,7 +671,7 @@ void ChangePanel(BaseVisualElementPanel p) using (var e = AttachToPanelEvent.GetPooled(prevPanel, p)) { e.target = this; - elementPanel.SendEvent(e, DispatchMode.Immediate); + elementPanel.SendEvent(e, DispatchMode.Default); } } diff --git a/Modules/UnityAnalytics/Public/UnityAnalytics.bindings.cs b/Modules/UnityAnalytics/Public/UnityAnalytics.bindings.cs index 7ac9f080a8..93fee2fff8 100644 --- a/Modules/UnityAnalytics/Public/UnityAnalytics.bindings.cs +++ b/Modules/UnityAnalytics/Public/UnityAnalytics.bindings.cs @@ -13,6 +13,41 @@ namespace UnityEngine.Analytics [NativeHeader("Modules/UnityAnalytics/Public/Events/UserCustomEvent.h")] public static partial class Analytics { + public static bool initializeOnStartup + { + get + { + if (!IsInitialized()) + return false; + return initializeOnStartupInternal; + } + set + { + if (IsInitialized()) + initializeOnStartupInternal = value; + } + } + + public static AnalyticsResult ResumeInitialization() + { + if (!IsInitialized()) + return AnalyticsResult.NotInitialized; + return ResumeInitializationInternal(); + } + + [StaticAccessor("GetUnityAnalytics()", StaticAccessorType.Dot)] + [NativeMethod("ResumeInitialization")] + private static extern AnalyticsResult ResumeInitializationInternal(); + + [StaticAccessor("GetUnityAnalytics()", StaticAccessorType.Dot)] + private extern static bool initializeOnStartupInternal + { + [NativeMethod("GetInitializeOnStartup")] + get; + [NativeMethod("SetInitializeOnStartup")] + set; + } + [ThreadSafe] private static extern bool IsInitialized(); diff --git a/Modules/UnityEditorAnalyticsEditor/EditorAnalytics.bindings.cs b/Modules/UnityEditorAnalyticsEditor/EditorAnalytics.bindings.cs index 5bd23f2bf8..56e7db50de 100644 --- a/Modules/UnityEditorAnalyticsEditor/EditorAnalytics.bindings.cs +++ b/Modules/UnityEditorAnalyticsEditor/EditorAnalytics.bindings.cs @@ -83,6 +83,8 @@ internal static bool SendCollabOperation(object parameters) return EditorAnalytics.SendEvent("collabOperation", parameters); } + internal extern static bool SendAssetDownloadEvent(object parameters); + public extern static bool enabled { get; diff --git a/Modules/UnityWebRequestAudio/Public/DownloadHandlerAudio.bindings.cs b/Modules/UnityWebRequestAudio/Public/DownloadHandlerAudio.bindings.cs index dbc8c4accb..75c3447496 100644 --- a/Modules/UnityWebRequestAudio/Public/DownloadHandlerAudio.bindings.cs +++ b/Modules/UnityWebRequestAudio/Public/DownloadHandlerAudio.bindings.cs @@ -40,6 +40,7 @@ protected override string GetText() throw new System.NotSupportedException("String access is not supported for audio clips"); } + [NativeThrows] public extern AudioClip audioClip { get; } public extern bool streamAudio { get; set; } diff --git a/Modules/VFX/Public/ScriptBindings/VFXEnums.bindings.cs b/Modules/VFX/Public/ScriptBindings/VFXEnums.bindings.cs index 998ba30d89..c1dc69eb80 100644 --- a/Modules/VFX/Public/ScriptBindings/VFXEnums.bindings.cs +++ b/Modules/VFX/Public/ScriptBindings/VFXEnums.bindings.cs @@ -63,7 +63,7 @@ internal enum VFXExpressionOperation // matrix operations TRSToMatrix, - InverseTRS, + InverseMatrix, ExtractPositionFromMatrix, ExtractAnglesFromMatrix, ExtractScaleFromMatrix, @@ -123,7 +123,10 @@ internal enum VFXExpressionOperation // Logical operations LogicalAnd, LogicalOr, - LogicalNot + LogicalNot, + + // This allows backward compatibility + InverseTRS = InverseMatrix, } internal enum VFXValueType diff --git a/Modules/VFX/Public/ScriptBindings/VisualEffect.bindings.cs b/Modules/VFX/Public/ScriptBindings/VisualEffect.bindings.cs index 2d107d7a9c..6581da1271 100644 --- a/Modules/VFX/Public/ScriptBindings/VisualEffect.bindings.cs +++ b/Modules/VFX/Public/ScriptBindings/VisualEffect.bindings.cs @@ -43,9 +43,29 @@ public VFXEventAttribute CreateVFXEventAttribute() return vfxEventAttribute; } - extern public void Play(VFXEventAttribute eventAttribute = null); - extern public void Stop(VFXEventAttribute eventAttribute = null); - extern public void SendEvent(string eventName, VFXEventAttribute eventAttribute = null); + extern public void Play(VFXEventAttribute eventAttribute); + + public void Play() + { + Play(null); + } + + extern public void Stop(VFXEventAttribute eventAttribute); + public void Stop() + { + Stop(null); + } + + extern public void SendEvent(int eventNameID, VFXEventAttribute eventAttribute); + public void SendEvent(string eventName) + { + SendEvent(Shader.PropertyToID(eventName), null); + } + + public void SendEvent(string eventName, VFXEventAttribute eventAttribute) + { + SendEvent(Shader.PropertyToID(eventName), eventAttribute); + } extern public void Reinit(); extern public void AdvanceOneFrame(); diff --git a/Modules/VFXEditor/Public/ScriptBindings/VFXMemorySerializer.bindings.cs b/Modules/VFXEditor/Public/ScriptBindings/VFXMemorySerializer.bindings.cs index a5d4a5cf31..4773276ced 100644 --- a/Modules/VFXEditor/Public/ScriptBindings/VFXMemorySerializer.bindings.cs +++ b/Modules/VFXEditor/Public/ScriptBindings/VFXMemorySerializer.bindings.cs @@ -19,8 +19,23 @@ internal static class VFXMemorySerializer { extern public static string StoreObjects(ScriptableObject[] objects); - [FreeFunction(Name = "VFXMemorySerializerBindings::Internal_ExtractObjects")] - extern public static ScriptableObject[] ExtractObjects(string data, bool asACopy); + extern public static byte[] StoreObjectsToByteArray(ScriptableObject[] objects, CompressionLevel compressionLevel = CompressionLevel.None); + + [FreeFunction(Name = "VFXMemorySerializerBindings::Internal_ExtractObjects_FromString")] + extern private static ScriptableObject[] ExtractObjects_FromString(string data, bool asACopy); + + [FreeFunction(Name = "VFXMemorySerializerBindings::Internal_ExtractObjects_FromByteArray")] + extern private static ScriptableObject[] ExtractObjects_FromByteArray(byte[] data, bool asACopy); + + public static ScriptableObject[] ExtractObjects(string data, bool asACopy) + { + return ExtractObjects_FromString(data, asACopy); + } + + public static ScriptableObject[] ExtractObjects(byte[] data, bool asACopy) + { + return ExtractObjects_FromByteArray(data, asACopy); + } [FreeFunction(Name = "VFXMemorySerializerBindings::Internal_DuplicateObjects")] extern public static ScriptableObject[] DuplicateObjects(ScriptableObject[] objects); diff --git a/Modules/VFXEditor/Public/ScriptBindings/VisualEffectUtility.bindings.cs b/Modules/VFXEditor/Public/ScriptBindings/VisualEffectUtility.bindings.cs index 255aba86b2..e0b19a60c6 100644 --- a/Modules/VFXEditor/Public/ScriptBindings/VisualEffectUtility.bindings.cs +++ b/Modules/VFXEditor/Public/ScriptBindings/VisualEffectUtility.bindings.cs @@ -56,5 +56,13 @@ static public Gradient GetExpressionGradient(VisualEffect effect, uint expressio [FreeFunction(Name = "VisualEffectUtility::GetExpressionGradient", ThrowsException = true)] static extern private void GetExpressionGradient([NotNull] VisualEffect effect, uint expressionIndex, Gradient gradient); + + extern public static bool renderBounds + { + [FreeFunction(Name = "VisualEffectUtility::GetRenderBounds")] + get; + [FreeFunction(Name = "VisualEffectUtility::SetRenderBounds")] + set; + } } } diff --git a/Modules/XR/ScriptBindings/XRInput.bindings.cs b/Modules/XR/ScriptBindings/XRInput.bindings.cs index 461c5f7a68..b836986d97 100644 --- a/Modules/XR/ScriptBindings/XRInput.bindings.cs +++ b/Modules/XR/ScriptBindings/XRInput.bindings.cs @@ -20,13 +20,11 @@ public struct HapticCapabilities : IEquatable bool m_SupportsImpulse; bool m_SupportsBuffer; uint m_BufferFrequencyHz; - uint m_BufferMaxSize; public uint numChannels { get { return m_NumChannels; } internal set { m_NumChannels = value; } } public bool supportsImpulse { get { return m_SupportsImpulse; } internal set { m_SupportsImpulse = value; } } public bool supportsBuffer { get { return m_SupportsBuffer; } internal set { m_SupportsBuffer = value; } } public uint bufferFrequencyHz { get { return m_BufferFrequencyHz; } internal set { m_BufferFrequencyHz = value; } } - public uint bufferMaxSize { get { return m_BufferMaxSize; } internal set { m_BufferMaxSize = value; } } public override bool Equals(object obj) { @@ -41,8 +39,7 @@ public bool Equals(HapticCapabilities other) return numChannels == other.numChannels && supportsImpulse == other.supportsImpulse && supportsBuffer == other.supportsBuffer && - bufferFrequencyHz == other.bufferFrequencyHz && - bufferMaxSize == other.bufferMaxSize; + bufferFrequencyHz == other.bufferFrequencyHz; } public override int GetHashCode() @@ -50,68 +47,78 @@ public override int GetHashCode() return numChannels.GetHashCode() ^ (supportsImpulse.GetHashCode() << 1) ^ (supportsBuffer.GetHashCode() >> 1) ^ - (bufferFrequencyHz.GetHashCode() << 2) ^ - (bufferMaxSize.GetHashCode() >> 2); + (bufferFrequencyHz.GetHashCode() << 2); + } + + public static bool operator==(HapticCapabilities a, HapticCapabilities b) + { + return a.Equals(b); + } + + public static bool operator!=(HapticCapabilities a, HapticCapabilities b) + { + return !(a == b); } } + [UsedByNativeCode] [StructLayout(LayoutKind.Sequential)] [NativeConditional("ENABLE_VR")] - public struct HapticState : IEquatable + [NativeHeader("Modules/XR/Subsystems/Input/Public/XRInputTrackingFacade.h")] + public struct InputDevice : IEquatable { - uint m_SamplesQueued; - uint m_SamplesAvailable; + private UInt64 m_DeviceId; - public uint samplesQueued { get { return m_SamplesQueued; } internal set { m_SamplesQueued = value; } } - public uint samplesAvailable { get { return m_SamplesAvailable; } internal set { m_SamplesAvailable = value; } } + internal InputDevice(UInt64 deviceId) { m_DeviceId = deviceId; } + + public bool IsValid { get { return InputTracking.IsDeviceValid(m_DeviceId); } } + + // Haptics + public bool SendHapticImpulse(uint channel, float amplitude, float duration = 1.0f) { return InputTracking.SendHapticImpulse(m_DeviceId, channel, amplitude, duration); } + public bool SendHapticBuffer(uint channel, byte[] buffer) { return InputTracking.SendHapticBuffer(m_DeviceId, channel, buffer); } + public bool TryGetHapticCapabilities(out HapticCapabilities capabilities) { return InputTracking.TryGetHapticCapabilities(m_DeviceId, out capabilities); } + public void StopHaptics() { InputTracking.StopHaptics(m_DeviceId); } public override bool Equals(object obj) { - if (!(obj is HapticState)) + if (!(obj is InputDevice)) return false; - return Equals((HapticState)obj); + return Equals((InputDevice)obj); } - public bool Equals(HapticState other) + public bool Equals(InputDevice other) { - return samplesQueued == other.samplesQueued && - samplesAvailable == other.samplesAvailable; + return m_DeviceId == other.m_DeviceId; } public override int GetHashCode() { - return samplesQueued.GetHashCode() ^ (samplesAvailable.GetHashCode() << 2); + return m_DeviceId.GetHashCode(); + } + + public static bool operator==(InputDevice a, InputDevice b) + { + return a.Equals(b); + } + + public static bool operator!=(InputDevice a, InputDevice b) + { + return !(a == b); } } [NativeHeader("Modules/XR/Subsystems/Input/Public/XRInputTrackingFacade.h")] [NativeConditional("ENABLE_VR")] [StaticAccessor("XRInputTrackingFacade::Get()", StaticAccessorType.Dot)] - public partial class InputHaptic + public partial class InputDevices { - [NativeConditional("ENABLE_VR")] - bool SendImpulse(XRNode node, uint channel, float amplitude) { return SendImpulse(node, channel, amplitude, 1.0f); } - - [NativeConditional("ENABLE_VR")] - [NativeMethod("SendHapticImpulse")] - extern public static bool SendImpulse(XRNode node, uint channel, float amplitude, [UnityEngine.Internal.DefaultValue("1.0f")] float frequency); - - [NativeConditional("ENABLE_VR")] - [NativeMethod("SendHapticBuffer")] - extern public static bool SendBuffer(XRNode node, uint channel, byte[] buffer); - - [NativeConditional("ENABLE_VR", "false")] - [NativeMethod("TryGetHapticCapabilities")] - extern public static bool TryGetCapabilities(XRNode node, out HapticCapabilities capabilities); - - [NativeConditional("ENABLE_VR", "false")] - [NativeMethod("TryGetHapticState")] - extern public static bool TryGetState(XRNode node, out HapticState state); - - [NativeConditional("ENABLE_VR")] - [NativeMethod("StopHaptics")] - extern public static void Stop(XRNode node); + [NativeConditional("ENABLE_VR", "InputDevice::Identity")] + public static InputDevice GetDeviceAtXRNode(XRNode node) + { + UInt64 deviceId = InputTracking.GetDeviceIdAtXRNode(node); + return new InputDevice(deviceId); + } } [NativeHeader("Modules/XR/Subsystems/Input/Public/XRInputTrackingFacade.h")] @@ -156,5 +163,13 @@ extern public static bool disablePositionalTracking [NativeName("SetPositionalTrackingDisabled")] set; } + + internal static extern bool SendHapticImpulse(UInt64 deviceId, uint channel, float amplitude, float duration); + internal static extern bool SendHapticBuffer(UInt64 deviceId, uint channel, byte[] buffer); + internal static extern bool TryGetHapticCapabilities(UInt64 deviceId, out HapticCapabilities capabilities); + internal static extern void StopHaptics(UInt64 deviceId); + + internal static extern bool IsDeviceValid(UInt64 deviceId); + internal static extern UInt64 GetDeviceIdAtXRNode(XRNode node); } } diff --git a/Projects/CSharp/UnityEditor.csproj b/Projects/CSharp/UnityEditor.csproj index 03dd7982eb..4adac421dd 100644 --- a/Projects/CSharp/UnityEditor.csproj +++ b/Projects/CSharp/UnityEditor.csproj @@ -4461,6 +4461,9 @@ Modules\ShortcutManagerEditor\IShortcutPriorityContext.cs + + Modules\ShortcutManagerEditor\KeyCombination.cs + Modules\ShortcutManagerEditor\PriorityContextAttribute.cs diff --git a/Projects/CSharp/UnityEngine.csproj b/Projects/CSharp/UnityEngine.csproj index b285e968a3..bfcca14532 100644 --- a/Projects/CSharp/UnityEngine.csproj +++ b/Projects/CSharp/UnityEngine.csproj @@ -792,6 +792,9 @@ Runtime\Export\Scripting\ExtensionOfNativeClassAttribute.cs + + Runtime\Export\Scripting\GarbageCollector.bindings.cs + Runtime\Export\Scripting\PreserveAttribute.cs @@ -1485,6 +1488,12 @@ Modules\Substance\SubstanceUtility.cs + + Modules\Terrain\Public\BrushTransform.cs + + + Modules\Terrain\Public\PaintContext.cs + Modules\Terrain\Public\Terrain.bindings.cs @@ -1503,12 +1512,21 @@ Modules\TextCore\Managed\AssemblyInfo.cs + + Modules\TextCore\Managed\FaceInfo.cs + Modules\TextCore\Managed\Glyph.cs + + Modules\TextCore\Managed\OpenTypeCommon.cs + Modules\TextCore\ScriptBindings\FontEngine.bindings.cs + + Modules\TextCore\ScriptBindings\GlyphMarshallingStruct.cs + Modules\TextRendering\FontStyle.cs diff --git a/README.md b/README.md index b8559e74df..777698aff2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Unity 2018.3.0a11 C# reference source code +## Unity 2018.3.0b7 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/Runtime/2D/Common/ScriptBindings/SpriteDataAccess.bindings.cs b/Runtime/2D/Common/ScriptBindings/SpriteDataAccess.bindings.cs index feea57170f..7db928fc8d 100644 --- a/Runtime/2D/Common/ScriptBindings/SpriteDataAccess.bindings.cs +++ b/Runtime/2D/Common/ScriptBindings/SpriteDataAccess.bindings.cs @@ -185,7 +185,7 @@ public static void SetBones(this Sprite sprite, SpriteBone[] src) extern private static SpriteChannelInfo GetBoneWeightsInfo(Sprite sprite); unsafe extern private static void SetBoneWeightsData(Sprite sprite, void* src, int count); - extern private static AtomicSafetyHandle GetSafetyHandle(this Sprite sprite); + extern internal static AtomicSafetyHandle GetSafetyHandle(this Sprite sprite); } [NativeHeader("Runtime/2D/Common/SpriteDataAccess.h")] diff --git a/Runtime/AR/Tango/ScriptBindings/Tango.bindings.cs b/Runtime/AR/Tango/ScriptBindings/Tango.bindings.cs index d075d899e2..9a40d7718b 100644 --- a/Runtime/AR/Tango/ScriptBindings/Tango.bindings.cs +++ b/Runtime/AR/Tango/ScriptBindings/Tango.bindings.cs @@ -13,24 +13,6 @@ namespace UnityEngine.XR.Tango { - // This must correspond to Tango::CoordinateFrame in TangoTypes.h - internal enum CoordinateFrame - { - GlobalWGS84 = 0, - AreaDescription, - StartOfService, - PreviousDevicePose, - Device, - IMU, - Display, - CameraColor, - CameraDepth, - CameraFisheye, - UUID, - Invalid, - MaxCoordinateFrameType - } - internal enum PoseStatus { Initializing = 0, @@ -41,31 +23,16 @@ internal enum PoseStatus [UsedByNativeCode] [NativeHeader("ARScriptingClasses.h")] - [StructLayout(LayoutKind.Explicit, Size = 8)] - internal struct CoordinateFramePair - { - [FieldOffset(0)] public CoordinateFrame baseFrame; - [FieldOffset(4)] public CoordinateFrame targetFrame; - } - - [UsedByNativeCode] - [NativeHeader("ARScriptingClasses.h")] - [StructLayout(LayoutKind.Explicit, Size = 92)] internal struct PoseData { - [FieldOffset(0)] public uint version; - [FieldOffset(8)] public double timestamp; - [FieldOffset(16)] public double orientation_x; - [FieldOffset(24)] public double orientation_y; - [FieldOffset(32)] public double orientation_z; - [FieldOffset(40)] public double orientation_w; - [FieldOffset(48)] public double translation_x; - [FieldOffset(56)] public double translation_y; - [FieldOffset(64)] public double translation_z; - [FieldOffset(72)] public PoseStatus statusCode; - [FieldOffset(76)] public CoordinateFramePair frame; - [FieldOffset(84)] public uint confidence; - [FieldOffset(88)] public float accuracy; + public double orientation_x; + public double orientation_y; + public double orientation_z; + public double orientation_w; + public double translation_x; + public double translation_y; + public double translation_z; + public PoseStatus statusCode; public Quaternion rotation { @@ -78,24 +45,15 @@ public Vector3 position } } - [NativeHeader("Runtime/AR/Tango/TangoScriptApi.h")] [NativeConditional("PLATFORM_ANDROID")] internal static partial class TangoInputTracking { - extern private static bool Internal_TryGetPoseAtTime(double time, ScreenOrientation screenOrientation, - CoordinateFrame baseFrame, CoordinateFrame targetFrame, out PoseData pose); - - internal static bool TryGetPoseAtTime(out PoseData pose, CoordinateFrame baseFrame, CoordinateFrame targetFrame, - double time, ScreenOrientation screenOrientation) - { - return Internal_TryGetPoseAtTime(time, screenOrientation, baseFrame, targetFrame, out pose); - } + extern private static bool Internal_TryGetPoseAtTime(out PoseData pose); - internal static bool TryGetPoseAtTime(out PoseData pose, CoordinateFrame baseFrame, CoordinateFrame targetFrame, - double time = 0.0) + internal static bool TryGetPoseAtTime(out PoseData pose) { - return Internal_TryGetPoseAtTime(time, Screen.orientation, baseFrame, targetFrame, out pose); + return Internal_TryGetPoseAtTime(out pose); } } } diff --git a/Runtime/Export/AnimationCurve.bindings.cs b/Runtime/Export/AnimationCurve.bindings.cs index e8f0051e65..9262cff54b 100644 --- a/Runtime/Export/AnimationCurve.bindings.cs +++ b/Runtime/Export/AnimationCurve.bindings.cs @@ -227,6 +227,12 @@ public static AnimationCurve Constant(float timeStart, float timeEnd, float valu // A straight Line starting at /timeStart/, /valueStart/ and ending at /timeEnd/, /valueEnd/ public static AnimationCurve Linear(float timeStart, float valueStart, float timeEnd, float valueEnd) { + if (timeStart == timeEnd) + { + Keyframe key = new Keyframe(timeStart, valueStart); + return new AnimationCurve(new Keyframe[] {key}); + } + float tangent = (valueEnd - valueStart) / (timeEnd - timeStart); Keyframe[] keys = { new Keyframe(timeStart, valueStart, 0.0F, tangent), new Keyframe(timeEnd, valueEnd, tangent, 0.0F) }; return new AnimationCurve(keys); @@ -235,6 +241,12 @@ public static AnimationCurve Linear(float timeStart, float valueStart, float tim // An ease-in and out curve starting at /timeStart/, /valueStart/ and ending at /timeEnd/, /valueEnd/. public static AnimationCurve EaseInOut(float timeStart, float valueStart, float timeEnd, float valueEnd) { + if (timeStart == timeEnd) + { + Keyframe key = new Keyframe(timeStart, valueStart); + return new AnimationCurve(new Keyframe[] {key}); + } + Keyframe[] keys = { new Keyframe(timeStart, valueStart, 0.0F, 0.0F), new Keyframe(timeEnd, valueEnd, 0.0F, 0.0F) }; return new AnimationCurve(keys); } diff --git a/Runtime/Export/AssemblyInfo.cs b/Runtime/Export/AssemblyInfo.cs index 8f7751eb50..b6102a5ea3 100644 --- a/Runtime/Export/AssemblyInfo.cs +++ b/Runtime/Export/AssemblyInfo.cs @@ -91,7 +91,7 @@ [assembly: InternalsVisibleTo("Unity.InternalAPIEngineBridgeDev.004")] [assembly: InternalsVisibleTo("Unity.InternalAPIEngineBridgeDev.005")] -[assembly: InternalsVisibleTo("Unity.Subsystem.Declaration")] +[assembly: InternalsVisibleTo("Unity.Subsystem.Registration")] // Note: Don't add InternalsVisibleTo for UnityEngine.UI, because it's editable by users and it shouldn't access internal UnityEngine methods diff --git a/Runtime/Export/Camera.bindings.cs b/Runtime/Export/Camera.bindings.cs index a34e8b2ccf..9620d45454 100644 --- a/Runtime/Export/Camera.bindings.cs +++ b/Runtime/Export/Camera.bindings.cs @@ -232,6 +232,9 @@ public static Camera[] allCameras } public static int GetAllCameras(Camera[] cameras) { + if (cameras == null) + throw new NullReferenceException(); + if (cameras.Length < allCamerasCount) throw new ArgumentException("Passed in array to fill with cameras is to small to hold the number of cameras. Use Camera.allCamerasCount to get the needed size."); return GetAllCamerasImpl(cameras); @@ -274,7 +277,7 @@ public bool RenderToCubemap(RenderTexture cubemap, int faceMask, MonoOrStereosco public void AddCommandBuffer(CameraEvent evt, CommandBuffer buffer) { - if (!Enum.IsDefined(typeof(CameraEvent), evt)) + if (!Rendering.CameraEventUtils.IsValid(evt)) throw new ArgumentException(string.Format(@"Invalid CameraEvent value ""{0}"".", (int)evt), "evt"); if (buffer == null) throw new NullReferenceException("buffer is null"); AddCommandBufferImpl(evt, buffer); @@ -282,7 +285,7 @@ public void AddCommandBuffer(CameraEvent evt, CommandBuffer buffer) public void AddCommandBufferAsync(CameraEvent evt, CommandBuffer buffer, ComputeQueueType queueType) { - if (!Enum.IsDefined(typeof(CameraEvent), evt)) + if (!Rendering.CameraEventUtils.IsValid(evt)) throw new ArgumentException(string.Format(@"Invalid CameraEvent value ""{0}"".", (int)evt), "evt"); if (buffer == null) throw new NullReferenceException("buffer is null"); AddCommandBufferAsyncImpl(evt, buffer, queueType); @@ -290,7 +293,7 @@ public void AddCommandBufferAsync(CameraEvent evt, CommandBuffer buffer, Compute public void RemoveCommandBuffer(CameraEvent evt, CommandBuffer buffer) { - if (!Enum.IsDefined(typeof(CameraEvent), evt)) + if (!Rendering.CameraEventUtils.IsValid(evt)) throw new ArgumentException(string.Format(@"Invalid CameraEvent value ""{0}"".", (int)evt), "evt"); if (buffer == null) throw new NullReferenceException("buffer is null"); RemoveCommandBufferImpl(evt, buffer); diff --git a/Runtime/Export/GI/DynamicGI.bindings.cs b/Runtime/Export/GI/DynamicGI.bindings.cs index 04a31065e6..b4e134a2a3 100644 --- a/Runtime/Export/GI/DynamicGI.bindings.cs +++ b/Runtime/Export/GI/DynamicGI.bindings.cs @@ -12,10 +12,13 @@ public sealed partial class DynamicGI { public static float indirectScale { get { return 0.0f; } set {} } public static float updateThreshold { get { return 0.0f; } set {} } + public static int materialUpdateTimeSlice { get { return 0; } set {} } public static void SetEmissive(Renderer renderer, Color color) {} public static void SetEnvironmentData(float[] input) {} public static bool synchronousMode { get { return false; } set {} } public static bool isConverged { get { return false; } } + + internal static int scheduledMaterialUpdatesCount { get { return 0; } } public static extern void UpdateEnvironment(); [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] diff --git a/Runtime/Export/Graphics.bindings.cs b/Runtime/Export/Graphics.bindings.cs index 76668a828b..906c6a225e 100644 --- a/Runtime/Export/Graphics.bindings.cs +++ b/Runtime/Export/Graphics.bindings.cs @@ -538,3 +538,28 @@ public extern UnityEngine.Rendering.SphericalHarmonicsL2[] bakedProbes public extern int cellCount {[NativeName("GetTetrahedraSize")] get; } } } + +namespace UnityEngine.Experimental.Rendering +{ + public enum WaitForPresentSyncPoint + { + BeginFrame = 0, + EndFrame = 1 + } + + public enum GraphicsJobsSyncPoint + { + EndOfFrame = 0, + AfterScriptUpdate = 1, + AfterScriptLateUpdate = 2, + WaitForPresent = 3 + }; + + public static partial class GraphicsDeviceSettings + { + [StaticAccessor("GetGfxDevice()", StaticAccessorType.Dot)] + extern public static WaitForPresentSyncPoint waitForPresentSyncPoint { get; set; } + [StaticAccessor("GetGfxDevice()", StaticAccessorType.Dot)] + extern public static GraphicsJobsSyncPoint graphicsJobsSyncPoint { get; set; } + } +} diff --git a/Runtime/Export/GraphicsEnums.cs b/Runtime/Export/GraphicsEnums.cs index 1546184f20..b1ac63f5e6 100644 --- a/Runtime/Export/GraphicsEnums.cs +++ b/Runtime/Export/GraphicsEnums.cs @@ -961,6 +961,17 @@ public enum CameraEvent AfterHaloAndLensFlares } + internal static class CameraEventUtils + { + const CameraEvent k_MinimumValue = CameraEvent.BeforeDepthTexture; + const CameraEvent k_MaximumValue = CameraEvent.AfterHaloAndLensFlares; + + public static bool IsValid(CameraEvent value) + { + return value >= k_MinimumValue && value <= k_MaximumValue; + } + } + // Keep in sync with RenderLightEventType in Runtime/Graphics/CommandBuffer/RenderingEvents.h public enum LightEvent { diff --git a/Runtime/Export/GraphicsManagers.bindings.cs b/Runtime/Export/GraphicsManagers.bindings.cs index d5315d7c90..0e85526478 100644 --- a/Runtime/Export/GraphicsManagers.bindings.cs +++ b/Runtime/Export/GraphicsManagers.bindings.cs @@ -84,6 +84,8 @@ private QualitySettings() {} extern public static int antiAliasing { get; set; } extern public static int asyncUploadTimeSlice { get; set; } extern public static int asyncUploadBufferSize { get; set; } + extern public static bool asyncUploadPersistentBuffer { get; set; } + extern public static bool realtimeReflectionProbes { get; set; } extern public static bool billboardsFaceCameraPosition { get; set; } diff --git a/Runtime/Export/Logger/DebugLogHandler.cs b/Runtime/Export/Logger/DebugLogHandler.cs index c60c63622a..a43a3a5d15 100644 --- a/Runtime/Export/Logger/DebugLogHandler.cs +++ b/Runtime/Export/Logger/DebugLogHandler.cs @@ -15,6 +15,9 @@ public void LogFormat(LogType logType, Object context, string format, params obj public void LogException(Exception exception, Object context) { + if (exception == null) + throw new ArgumentNullException("exception"); + Internal_LogException(exception, context); } } diff --git a/Runtime/Export/PlayerLoop.bindings.cs b/Runtime/Export/PlayerLoop.bindings.cs index 19ae77c163..72314113da 100644 --- a/Runtime/Export/PlayerLoop.bindings.cs +++ b/Runtime/Export/PlayerLoop.bindings.cs @@ -176,7 +176,7 @@ public struct UpdateMasterServerInterface {} [RequiredByNativeCode] public struct UNetUpdate {} [RequiredByNativeCode] - public struct EndGraphicsJobsLate {} + public struct EndGraphicsJobsAfterScriptUpdate {} [RequiredByNativeCode] public struct ParticleSystemBeginUpdateAll {} [RequiredByNativeCode] @@ -208,6 +208,8 @@ public struct VFXUpdate {} [RequiredByNativeCode] public struct ParticleSystemEndUpdateAll {} [RequiredByNativeCode] + public struct EndGraphicsJobsAfterScriptLateUpdate {} + [RequiredByNativeCode] public struct UpdateSubstance {} [RequiredByNativeCode] public struct UpdateCustomRenderTextures {} diff --git a/Runtime/Export/RenderPipeline/CullingParameters.cs b/Runtime/Export/RenderPipeline/CullingParameters.cs index 95debed7c2..b2fe4d9e91 100644 --- a/Runtime/Export/RenderPipeline/CullingParameters.cs +++ b/Runtime/Export/RenderPipeline/CullingParameters.cs @@ -65,6 +65,7 @@ unsafe public struct CameraProperties CoreCameraValues coreCameraValues; uint cameraType; + private int projectionIsOblique; public Plane GetShadowCullingPlane(int index) { diff --git a/Runtime/Export/Scripting/GarbageCollector.bindings.cs b/Runtime/Export/Scripting/GarbageCollector.bindings.cs new file mode 100644 index 0000000000..3ed1d0271e --- /dev/null +++ b/Runtime/Export/Scripting/GarbageCollector.bindings.cs @@ -0,0 +1,46 @@ +// 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.Bindings; + +namespace UnityEngine.Scripting +{ + [NativeHeader("Runtime/Scripting/GarbageCollector.h")] + [VisibleToOtherModules] + public static class GarbageCollector + { + public enum Mode + { + Disabled = 0, + Enabled = 1, + } + + public static Action GCModeChanged; + + public static Mode GCMode + { + get + { + return GetMode(); + } + + set + { + if (value == GetMode()) + return; + + SetMode(value); + + if (GCModeChanged != null) + GCModeChanged(value); + } + } + + [NativeThrows] + extern static void SetMode(Mode mode); + [NativeThrows] + extern static Mode GetMode(); + } +} diff --git a/Runtime/Export/SystemInfo.bindings.cs b/Runtime/Export/SystemInfo.bindings.cs index 6125bb74a1..23fa6dc4ed 100644 --- a/Runtime/Export/SystemInfo.bindings.cs +++ b/Runtime/Export/SystemInfo.bindings.cs @@ -214,6 +214,11 @@ public static bool graphicsMultiThreaded get { return GetGraphicsMultiThreaded(); } } + public static bool hasHiddenSurfaceRemovalOnGPU + { + get { return HasHiddenSurfaceRemovalOnGPU(); } + } + // Are built-in shadows supported? (RO) public static bool supportsShadows { @@ -527,6 +532,9 @@ public static int graphicsPixelFillrate [FreeFunction("ScriptingGraphicsCaps::GetGraphicsMultiThreaded")] static extern bool GetGraphicsMultiThreaded(); + [FreeFunction("ScriptingGraphicsCaps::HasHiddenSurfaceRemovalOnGPU")] + static extern bool HasHiddenSurfaceRemovalOnGPU(); + [FreeFunction("ScriptingGraphicsCaps::SupportsShadows")] static extern bool SupportsShadows(); diff --git a/Runtime/Export/Texture.cs b/Runtime/Export/Texture.cs index 06d0730b91..972e0c25ee 100644 --- a/Runtime/Export/Texture.cs +++ b/Runtime/Export/Texture.cs @@ -739,6 +739,7 @@ public void Apply([uei.DefaultValue("true")] bool updateMipmaps, [uei.DefaultVal public sealed partial class CubemapArray : Texture { + [RequiredByNativeCode] public CubemapArray(int width, int cubemapCount, GraphicsFormat format, TextureCreationFlags flags) { if (ValidateFormat(format, FormatUsage.Sample)) diff --git a/Runtime/Export/Vector3Int.cs b/Runtime/Export/Vector3Int.cs index d7871a42a8..15e21aa1f8 100644 --- a/Runtime/Export/Vector3Int.cs +++ b/Runtime/Export/Vector3Int.cs @@ -177,7 +177,9 @@ public bool Equals(Vector3Int other) public override int GetHashCode() { - return x.GetHashCode() ^ (y.GetHashCode() << 2) ^ (z.GetHashCode() >> 2); + var yHash = y.GetHashCode(); + var zHash = z.GetHashCode(); + return x.GetHashCode() ^ (yHash << 4) ^ (yHash >> 28) ^ (zHash >> 4) ^ (zHash << 28); } public override string ToString() diff --git a/Runtime/Networking/Managed/MatchMakingClient.cs b/Runtime/Networking/Managed/MatchMakingClient.cs index 79df91f44d..4725689b0a 100644 --- a/Runtime/Networking/Managed/MatchMakingClient.cs +++ b/Runtime/Networking/Managed/MatchMakingClient.cs @@ -12,6 +12,7 @@ namespace UnityEngine.Networking.Match { // returned when you create or join a match (private info) //[Serializable] //TODO: enabled this when 64 bit enum issue is resolved + [Obsolete("The matchmaker and relay feature will be removed in the future, minimal support will continue until this can be safely done.")] public class MatchInfo { public string address { get; private set; } @@ -53,6 +54,7 @@ public override string ToString() } } + [Obsolete("The matchmaker and relay feature will be removed in the future, minimal support will continue until this can be safely done.")] public class MatchInfoSnapshot { public NetworkID networkId { get; private set; } @@ -109,6 +111,7 @@ internal MatchInfoSnapshot(MatchDesc matchDesc) } } + [Obsolete("The matchmaker and relay feature will be removed in the future, minimal support will continue until this can be safely done.")] public class NetworkMatch : MonoBehaviour { public delegate void BasicResponseDelegate(bool success, string extendedInfo); @@ -137,11 +140,9 @@ public Coroutine CreateMatch(string matchName, uint matchSize, bool matchAdverti Debug.LogError("Matchmaking is not supported on WebGL player."); return null; } - else - return CreateMatch(new CreateMatchRequest { name = matchName, size = matchSize, advertise = matchAdvertise, password = matchPassword, publicAddress = publicClientAddress, privateAddress = privateClientAddress, eloScore = eloScoreForMatch, domain = requestDomain }, callback); + return CreateMatch(new CreateMatchRequest { name = matchName, size = matchSize, advertise = matchAdvertise, password = matchPassword, publicAddress = publicClientAddress, privateAddress = privateClientAddress, eloScore = eloScoreForMatch, domain = requestDomain }, callback); } - // Begin Create a match internal Coroutine CreateMatch(CreateMatchRequest req, DataResponseDelegate callback) { if (callback == null) diff --git a/Runtime/Networking/Managed/NetworkTransportConfig.cs b/Runtime/Networking/Managed/NetworkTransportConfig.cs index 9bdf9f3b57..84450263db 100644 --- a/Runtime/Networking/Managed/NetworkTransportConfig.cs +++ b/Runtime/Networking/Managed/NetworkTransportConfig.cs @@ -68,6 +68,7 @@ public enum ConnectionAcksType }; [Serializable] + [Obsolete("The UNET transport will be removed in the future as soon a replacement is ready.")] public class ChannelQOS { [SerializeField] @@ -105,6 +106,7 @@ public ChannelQOS(ChannelQOS channel) //to allow user manipulate channel info via property with parameter (using array interface) //all fields are defined for direct access from HLAPI (it it needed) [Serializable] + [Obsolete("The UNET transport will be removed in the future as soon a replacement is ready.")] public class ConnectionConfig { private const int g_MinPacketSize = 128; @@ -502,6 +504,7 @@ public IList GetSharedOrderChannels(byte idx) //and array of special connection (with special configuration) //AddSpecialConnection will return connection id which user should use when he call connect to identify special configuration for this connection [Serializable] + [Obsolete("The UNET transport will be removed in the future as soon a replacement is ready.")] public class HostTopology { [SerializeField] @@ -589,6 +592,7 @@ public int AddSpecialConnectionConfig(ConnectionConfig config) } [Serializable] + [Obsolete("The UNET transport will be removed in the future as soon a replacement is ready.")] public class GlobalConfig { private const uint g_MaxTimerTimeout = 12000; //before changing check UNETConfiguration.h file @@ -753,6 +757,7 @@ public Action ConnectionReadyForSend } } + [Obsolete("The UNET transport will be removed in the future as soon a replacement is ready.")] public class ConnectionSimulatorConfig : IDisposable { internal int m_OutMinDelay; diff --git a/Runtime/Networking/ScriptBindings/UNETTransportConfig.bindings.cs b/Runtime/Networking/ScriptBindings/UNETTransportConfig.bindings.cs index a170c1b9ed..10e043cef8 100644 --- a/Runtime/Networking/ScriptBindings/UNETTransportConfig.bindings.cs +++ b/Runtime/Networking/ScriptBindings/UNETTransportConfig.bindings.cs @@ -13,6 +13,7 @@ namespace UnityEngine.Networking { +#pragma warning disable 618 [NativeHeader("Runtime/Networking/UNETManager.h")] [NativeHeader("Runtime/Networking/UNetTypes.h")] @@ -308,4 +309,5 @@ public void Dispose() [NativeProperty("m_MaxNetSimulatorTimeout", TargetType.Field)] private extern uint MaxNetSimulatorTimeout { set; } } +#pragma warning restore 618 } diff --git a/Runtime/Networking/ScriptBindings/UNETworking.bindings.cs b/Runtime/Networking/ScriptBindings/UNETworking.bindings.cs index c5eafd5400..0d8523ddf3 100644 --- a/Runtime/Networking/ScriptBindings/UNETworking.bindings.cs +++ b/Runtime/Networking/ScriptBindings/UNETworking.bindings.cs @@ -19,6 +19,7 @@ namespace UnityEngine.Networking [NativeHeader("Runtime/Networking/UNetTypes.h")] [NativeHeader("Runtime/Networking/UNETConfiguration.h")] [NativeConditional("ENABLE_NETWORK && ENABLE_UNET", true)] + [Obsolete("The UNET transport will be removed in the future as soon a replacement is ready.")] public sealed partial class NetworkTransport { private NetworkTransport() {} diff --git a/Runtime/ParticleSystem/Managed/ParticleSystem.deprecated.cs b/Runtime/ParticleSystem/Managed/ParticleSystem.deprecated.cs index 462a1bf79b..a0611e5bc6 100644 --- a/Runtime/ParticleSystem/Managed/ParticleSystem.deprecated.cs +++ b/Runtime/ParticleSystem/Managed/ParticleSystem.deprecated.cs @@ -44,7 +44,7 @@ public partial struct ShapeModule // Scale. (Meshes) [Obsolete("meshScale property is deprecated.Please use scale instead.", false)] public float meshScale { get { return scale.x; } set { scale = new Vector3(value, value, value); } } - [Obsolete("randomDirection property is deprecated.Use randomDirectionAmount instead.", false)] + [Obsolete("randomDirection property is deprecated. Use randomDirectionAmount instead.", false)] public bool randomDirection { get { return (randomDirectionAmount >= 0.5f); } set { randomDirectionAmount = value ? 1.0f : 0.0f; } } } @@ -82,11 +82,11 @@ public partial struct Particle { [Obsolete("Please use Particle.remainingLifetime instead. (UnityUpgradable) -> UnityEngine.ParticleSystem/Particle.remainingLifetime", false)] public float lifetime { get { return remainingLifetime; } set { remainingLifetime = value; } } - [Obsolete("randomValue property is deprecated.Use randomSeed instead to control random behavior of particles.", false)] + [Obsolete("randomValue property is deprecated. Use randomSeed instead to control random behavior of particles.", false)] public float randomValue { get { return BitConverter.ToSingle(BitConverter.GetBytes(m_RandomSeed), 0); } set { m_RandomSeed = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); } } - [Obsolete("size property is deprecated.Use startSize or GetCurrentSize() instead.", false)] + [Obsolete("size property is deprecated. Use startSize or GetCurrentSize() instead.", false)] public float size { get { return startSize; } set { startSize = value; } } - [Obsolete("color property is deprecated.Use startColor or GetCurrentColor() instead.", false)] + [Obsolete("color property is deprecated. Use startColor or GetCurrentColor() instead.", false)] public Color32 color { get { return startColor; } set { startColor = value; } } } @@ -125,56 +125,59 @@ public void Emit(ParticleSystem.Particle particle) Internal_EmitOld(ref particle); } - [Obsolete("startDelay property is deprecated.Use main.startDelay or main.startDelayMultiplier instead.", false)] + [Obsolete("startDelay property is deprecated. Use main.startDelay or main.startDelayMultiplier instead.", false)] public float startDelay { get { return main.startDelayMultiplier; } set { var m = main; m.startDelayMultiplier = value; } } - [Obsolete("loop property is deprecated.Use main.loop instead.", false)] + [Obsolete("loop property is deprecated. Use main.loop instead.", false)] public bool loop { get { return main.loop; } set { var m = main; m.loop = value; } } - [Obsolete("playOnAwake property is deprecated.Use main.playOnAwake instead.", false)] + [Obsolete("playOnAwake property is deprecated. Use main.playOnAwake instead.", false)] public bool playOnAwake { get { return main.playOnAwake; } set { var m = main; m.playOnAwake = value; } } - [Obsolete("duration property is deprecated.Use main.duration instead.", false)] + [Obsolete("duration property is deprecated. Use main.duration instead.", false)] public float duration { get { return main.duration; } } - [Obsolete("playbackSpeed property is deprecated.Use main.simulationSpeed instead.", false)] + [Obsolete("playbackSpeed property is deprecated. Use main.simulationSpeed instead.", false)] public float playbackSpeed { get { return main.simulationSpeed; } set { var m = main; m.simulationSpeed = value; } } - [Obsolete("enableEmission property is deprecated.Use emission.enabled instead.", false)] + [Obsolete("enableEmission property is deprecated. Use emission.enabled instead.", false)] public bool enableEmission { get { return emission.enabled; } set { var em = emission; em.enabled = value; } } - [Obsolete("emissionRate property is deprecated.Use emission.rateOverTime, emission.rateOverDistance, emission.rateOverTimeMultiplier or emission.rateOverDistanceMultiplier instead.", false)] + [Obsolete("emissionRate property is deprecated. Use emission.rateOverTime, emission.rateOverDistance, emission.rateOverTimeMultiplier or emission.rateOverDistanceMultiplier instead.", false)] public float emissionRate { get { return emission.rateOverTimeMultiplier; } set { var em = emission; em.rateOverTime = value; } } - [Obsolete("startSpeed property is deprecated.Use main.startSpeed or main.startSpeedMultiplier instead.", false)] + [Obsolete("startSpeed property is deprecated. Use main.startSpeed or main.startSpeedMultiplier instead.", false)] public float startSpeed { get { return main.startSpeedMultiplier; } set { var m = main; m.startSpeedMultiplier = value; } } - [Obsolete("startSize property is deprecated.Use main.startSize or main.startSizeMultiplier instead.", false)] + [Obsolete("startSize property is deprecated. Use main.startSize or main.startSizeMultiplier instead.", false)] public float startSize { get { return main.startSizeMultiplier; } set { var m = main; m.startSizeMultiplier = value; } } - [Obsolete("startColor property is deprecated.Use main.startColor instead.", false)] + [Obsolete("startColor property is deprecated. Use main.startColor instead.", false)] public Color startColor { get { return main.startColor.color; } set { var m = main; m.startColor = value; } } - [Obsolete("startRotation property is deprecated.Use main.startRotation or main.startRotationMultiplier instead.", false)] + [Obsolete("startRotation property is deprecated. Use main.startRotation or main.startRotationMultiplier instead.", false)] public float startRotation { get { return main.startRotationMultiplier; } set { var m = main; m.startRotationMultiplier = value; } } - [Obsolete("startRotation3D property is deprecated.Use main.startRotationX, main.startRotationY and main.startRotationZ instead. (Or main.startRotationXMultiplier, main.startRotationYMultiplier and main.startRotationZMultiplier).", false)] + [Obsolete("startRotation3D property is deprecated. Use main.startRotationX, main.startRotationY and main.startRotationZ instead. (Or main.startRotationXMultiplier, main.startRotationYMultiplier and main.startRotationZMultiplier).", false)] public Vector3 startRotation3D { get { return new Vector3(main.startRotationXMultiplier, main.startRotationYMultiplier, main.startRotationZMultiplier); } set { var m = main; m.startRotationXMultiplier = value.x; m.startRotationYMultiplier = value.y; m.startRotationZMultiplier = value.z; } } - [Obsolete("startLifetime property is deprecated.Use main.startLifetime or main.startLifetimeMultiplier instead.", false)] + [Obsolete("startLifetime property is deprecated. Use main.startLifetime or main.startLifetimeMultiplier instead.", false)] public float startLifetime { get { return main.startLifetimeMultiplier; } set { var m = main; m.startLifetimeMultiplier = value; } } - [Obsolete("gravityModifier property is deprecated.Use main.gravityModifier or main.gravityModifierMultiplier instead.", false)] + [Obsolete("gravityModifier property is deprecated. Use main.gravityModifier or main.gravityModifierMultiplier instead.", false)] public float gravityModifier { get { return main.gravityModifierMultiplier; } set { var m = main; m.gravityModifierMultiplier = value; } } - [Obsolete("maxParticles property is deprecated.Use main.maxParticles instead.", false)] + [Obsolete("maxParticles property is deprecated. Use main.maxParticles instead.", false)] public int maxParticles { get { return main.maxParticles; } set { var m = main; m.maxParticles = value; } } - [Obsolete("simulationSpace property is deprecated.Use main.simulationSpace instead.", false)] + [Obsolete("simulationSpace property is deprecated. Use main.simulationSpace instead.", false)] public ParticleSystemSimulationSpace simulationSpace { get { return main.simulationSpace; } set { var m = main; m.simulationSpace = value; } } - [Obsolete("scalingMode property is deprecated.Use main.scalingMode instead.", false)] + [Obsolete("scalingMode property is deprecated. Use main.scalingMode instead.", false)] public ParticleSystemScalingMode scalingMode { get { return main.scalingMode; } set { var m = main; m.scalingMode = value; } } + + [Obsolete("automaticCullingEnabled property is deprecated. Use proceduralSimulationSupported instead (UnityUpgradable) -> proceduralSimulationSupported", true)] + public bool automaticCullingEnabled { get { return proceduralSimulationSupported; } } } public static partial class ParticlePhysicsExtensions diff --git a/Runtime/ParticleSystem/Managed/ParticleSystemEnums.cs b/Runtime/ParticleSystem/Managed/ParticleSystemEnums.cs index c1a37bb876..432ca9b435 100644 --- a/Runtime/ParticleSystem/Managed/ParticleSystemEnums.cs +++ b/Runtime/ParticleSystem/Managed/ParticleSystemEnums.cs @@ -188,6 +188,15 @@ public enum ParticleSystemStopAction Callback = 3 // Calls OnParticleSystemStopped. } + // The action to perform when a particle system is offscreen + public enum ParticleSystemCullingMode + { + Automatic = 0, + PauseAndCatchup = 1, + Pause = 2, + AlwaysSimulate = 3 + } + // The emitter velocity mode for particle systems public enum ParticleSystemEmitterVelocityMode { diff --git a/Runtime/ParticleSystem/ScriptBindings/ParticleSystem.bindings.cs b/Runtime/ParticleSystem/ScriptBindings/ParticleSystem.bindings.cs index cde478662b..f1fe7ae225 100644 --- a/Runtime/ParticleSystem/ScriptBindings/ParticleSystem.bindings.cs +++ b/Runtime/ParticleSystem/ScriptBindings/ParticleSystem.bindings.cs @@ -14,6 +14,7 @@ namespace UnityEngine { [NativeHeader("ParticleSystemScriptingClasses.h")] [NativeHeader("Runtime/ParticleSystem/ParticleSystem.h")] + [NativeHeader("Runtime/ParticleSystem/ParticleSystemGeometryJob.h")] [NativeHeader("Runtime/ParticleSystem/ScriptBindings/ParticleSystemScriptBindings.h")] [RequireComponent(typeof(Transform))] public partial class ParticleSystem : Component @@ -21,30 +22,30 @@ public partial class ParticleSystem : Component // Properties extern public bool isPlaying { - [NativeName("SyncJobs()->IsPlaying")] get; + [NativeName("SyncJobs(false)->IsPlaying")] get; } extern public bool isEmitting { - [NativeName("SyncJobs()->IsEmitting")] get; + [NativeName("SyncJobs(false)->IsEmitting")] get; } extern public bool isStopped { - [NativeName("SyncJobs()->IsStopped")] get; + [NativeName("SyncJobs(false)->IsStopped")] get; } extern public bool isPaused { - [NativeName("SyncJobs()->IsPaused")] get; + [NativeName("SyncJobs(false)->IsPaused")] get; } extern public int particleCount { - [NativeName("SyncJobs()->GetParticleCount")] get; + [NativeName("SyncJobs(false)->GetParticleCount")] get; } extern public float time { - [NativeName("SyncJobs()->GetSecPosition")] + [NativeName("SyncJobs(false)->GetSecPosition")] get; - [NativeName("SyncJobs()->SetSecPosition")] + [NativeName("SyncJobs(false)->SetSecPosition")] set; } @@ -52,7 +53,7 @@ extern public UInt32 randomSeed { [NativeName("GetRandomSeed")] get; - [NativeName("SyncJobs()->SetRandomSeed")] + [NativeName("SyncJobs(false)->SetRandomSeed")] set; } @@ -60,11 +61,11 @@ extern public bool useAutoRandomSeed { [NativeName("GetAutoRandomSeed")] get; - [NativeName("SyncJobs()->SetAutoRandomSeed")] + [NativeName("SyncJobs(false)->SetAutoRandomSeed")] set; } - extern public bool automaticCullingEnabled + extern public bool proceduralSimulationSupported { get; } @@ -283,6 +284,9 @@ public CustomDataModule customData [NativeName(Name = "SyncJobs()->EmitParticlesExternal")] extern public void Emit(ParticleSystem.EmitParams emitParams, int count); + [FreeFunction(Name = "ParticleSystemGeometryJob::ResetPreMappedBufferMemory")] + extern public static void ResetPreMappedBufferMemory(); + [FreeFunction(Name = "ParticleSystemEditor::SetupDefaultParticleSystemType", HasExplicitThis = true)] extern internal void SetupDefaultType(ParticleSystemSubEmitterType type); @@ -304,10 +308,10 @@ extern internal Matrix4x4 localToWorldMatrix extern internal bool CalculateEffectUISubEmitterData(ref int particleCount, ref float fastestParticle, ref float slowestParticle); [FreeFunction(Name = "ParticleSystemScriptBindings::CheckVertexStreamsMatchShader")] - extern static internal bool CheckVertexStreamsMatchShader(bool hasTangent, bool hasColor, int texCoordChannelCount, Material material, ref bool tangentError, ref bool colorError, ref bool uvError); + extern internal static bool CheckVertexStreamsMatchShader(bool hasTangent, bool hasColor, int texCoordChannelCount, Material material, ref bool tangentError, ref bool colorError, ref bool uvError); [FreeFunction(Name = "ParticleSystemScriptBindings::GetMaxTexCoordStreams")] - extern static internal int GetMaxTexCoordStreams(); + extern internal static int GetMaxTexCoordStreams(); } } diff --git a/Runtime/Profiler/ScriptBindings/MemoryProfiling.bindings.cs b/Runtime/Profiler/ScriptBindings/MemoryProfiling.bindings.cs index dc9144cefa..38177b0d47 100644 --- a/Runtime/Profiler/ScriptBindings/MemoryProfiling.bindings.cs +++ b/Runtime/Profiler/ScriptBindings/MemoryProfiling.bindings.cs @@ -61,6 +61,7 @@ public static void TakeTempSnapshot(Action finishCallback, Captur TakeSnapshot(path, finishCallback, captureFlags); } + [RequiredByNativeCode] static byte[] PrepareMetadata() { if (createMetaData == null) @@ -71,6 +72,9 @@ static byte[] PrepareMetadata() MetaData data = new MetaData(); createMetaData(data); + if (data.content == null) data.content = ""; + if (data.platform == null) data.platform = ""; + int contentLength = sizeof(char) * data.content.Length; int platformLength = sizeof(char) * data.platform.Length; @@ -164,6 +168,7 @@ private static int WriteStringToByteArray(byte[] array, int offset, string value return offset; } + [RequiredByNativeCode] static void FinalizeSnapshot(string path, bool result) { if (snapshotFinished != null) diff --git a/Runtime/Profiler/ScriptBindings/Profiler.bindings.cs b/Runtime/Profiler/ScriptBindings/Profiler.bindings.cs index 8ff99cecec..9173dfda3e 100644 --- a/Runtime/Profiler/ScriptBindings/Profiler.bindings.cs +++ b/Runtime/Profiler/ScriptBindings/Profiler.bindings.cs @@ -162,7 +162,7 @@ public static void BeginSample(string name, Object targetObject) // End profiling a piece of code with a custom label. // TODO: make obsolete //OBSOLETE warning Profiler.EndSample method is deprecated. Please use faster CustomSampler.End method instead. - [NativeConditional("ENABLE_PROFILER")] + [Conditional("ENABLE_PROFILER")] [NativeMethod(Name = "ProfilerBindings::EndSample", IsFreeFunction = true, IsThreadSafe = true)] public extern static void EndSample(); diff --git a/Runtime/Transform/ScriptBindings/Transform.bindings.cs b/Runtime/Transform/ScriptBindings/Transform.bindings.cs index 79877fee02..3e3e121490 100644 --- a/Runtime/Transform/ScriptBindings/Transform.bindings.cs +++ b/Runtime/Transform/ScriptBindings/Transform.bindings.cs @@ -382,7 +382,10 @@ public int hierarchyCapacity [FreeFunction("SetHierarchyCapacity", HasExplicitThis = true)] private extern void internal_setHierarchyCapacity(int value); - public int hierarchyCount { get; } + public int hierarchyCount { get { return internal_getHierarchyCount(); } } + + [FreeFunction("GetHierarchyCount", HasExplicitThis = true)] + private extern int internal_getHierarchyCount(); [NativeConditional("UNITY_EDITOR")] [FreeFunction("IsNonUniformScaleTransform", HasExplicitThis = true)] diff --git a/artifacts/generated/bindings_old/common/Editor/ModuleMetadataBindings.gen.cs b/artifacts/generated/bindings_old/common/Editor/ModuleMetadataBindings.gen.cs index 91ff96bb6b..51484d3ada 100644 --- a/artifacts/generated/bindings_old/common/Editor/ModuleMetadataBindings.gen.cs +++ b/artifacts/generated/bindings_old/common/Editor/ModuleMetadataBindings.gen.cs @@ -54,6 +54,14 @@ public static UnityType[] GetModuleTypes(string moduleName) [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] extern internal static ModuleIncludeSetting GetModuleIncludeSettingForObject (Object o) ; + [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration + [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] + extern internal static string GetExcludingModuleForObject (Object o) ; + + [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration + [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] + extern internal static string GetExcludingModule (string module) ; + [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] extern internal static uint[] GetModuleTypeIndices (string moduleName) ; diff --git a/artifacts/generated/bindings_old/common/ParticleSystem/ParticleSystemBindings.gen.cs b/artifacts/generated/bindings_old/common/ParticleSystem/ParticleSystemBindings.gen.cs index a95bdccce4..8e66c9141b 100644 --- a/artifacts/generated/bindings_old/common/ParticleSystem/ParticleSystemBindings.gen.cs +++ b/artifacts/generated/bindings_old/common/ParticleSystem/ParticleSystemBindings.gen.cs @@ -63,6 +63,7 @@ public partial struct MainModule public int maxParticles { get { return GetMaxParticles(m_ParticleSystem); } set { SetMaxParticles(m_ParticleSystem, value); } } public ParticleSystemEmitterVelocityMode emitterVelocityMode { get { return GetUseRigidbodyForVelocity(m_ParticleSystem) ? ParticleSystemEmitterVelocityMode.Rigidbody : ParticleSystemEmitterVelocityMode.Transform; } set { SetUseRigidbodyForVelocity(m_ParticleSystem, value == ParticleSystemEmitterVelocityMode.Rigidbody); } } public ParticleSystemStopAction stopAction { get { return GetStopAction(m_ParticleSystem); } set { SetStopAction(m_ParticleSystem, value); } } + public ParticleSystemCullingMode cullingMode { get { return GetCullingMode(m_ParticleSystem); } set { SetCullingMode(m_ParticleSystem, value); } } public ParticleSystemRingBufferMode ringBufferMode { get { return GetRingBufferMode(m_ParticleSystem); } set { SetRingBufferMode(m_ParticleSystem, value); } } public Vector2 ringBufferLoopRange { get { return GetRingBufferLoopRange(m_ParticleSystem); } set { SetRingBufferLoopRange(m_ParticleSystem, value); } } @@ -363,6 +364,14 @@ public partial struct MainModule [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] extern private static ParticleSystemStopAction GetStopAction (ParticleSystem system) ; + [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration + [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] + extern private static void SetCullingMode (ParticleSystem system, ParticleSystemCullingMode value) ; + + [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration + [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] + extern private static ParticleSystemCullingMode GetCullingMode (ParticleSystem system) ; + [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] extern private static void SetRingBufferMode (ParticleSystem system, ParticleSystemRingBufferMode value) ; @@ -2962,6 +2971,8 @@ public partial struct TrailModule public bool generateLightingData { set { SetGenerateLightingData(m_ParticleSystem, value); } get { return GetGenerateLightingData(m_ParticleSystem); } } public int ribbonCount { get { return GetRibbonCount(m_ParticleSystem); } set { SetRibbonCount(m_ParticleSystem, value); } } public float shadowBias { set { SetShadowBias(m_ParticleSystem, value); } get { return GetShadowBias(m_ParticleSystem); } } + public bool splitSubEmitterRibbons { set { SetSplitSubEmitterRibbons(m_ParticleSystem, value); } get { return GetSplitSubEmitterRibbons(m_ParticleSystem); } } + public bool attachRibbonsToTransform { set { SetAttachRibbonsToTransform(m_ParticleSystem, value); } get { return GetAttachRibbonsToTransform(m_ParticleSystem); } } [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration @@ -3116,6 +3127,22 @@ public partial struct TrailModule [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] extern private static float GetShadowBias (ParticleSystem system) ; + [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration + [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] + extern private static void SetSplitSubEmitterRibbons (ParticleSystem system, bool value) ; + + [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration + [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] + extern private static bool GetSplitSubEmitterRibbons (ParticleSystem system) ; + + [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration + [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] + extern private static void SetAttachRibbonsToTransform (ParticleSystem system, bool value) ; + + [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration + [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] + extern private static bool GetAttachRibbonsToTransform (ParticleSystem system) ; + } [System.Runtime.InteropServices.StructLayout (System.Runtime.InteropServices.LayoutKind.Sequential)]