diff --git a/CHANGELOG.md b/CHANGELOG.md index 16effc9..777fbad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,60 @@ # Changelog These are the release notes for the TextMesh Pro UPM package which was first introduced with Unity 2018.1. Please see the following link for the Release Notes for prior versions of TextMesh Pro. http://digitalnativestudios.com/forum/index.php?topic=1363.0 +## [3.2.0-pre.1] - 2021-08-06 +## [2.2.0-preview.1] - 2021-08-06 +## [1.6.0-preview.1] - 2021-08-06 +### Changes +- Added support for manually defining Diacritical Mark-to-Base and Mark-to-Mark glyph positional adjustments in font assets. +- Updated the font asset editor to include new Mark-to-Base and Mark-to-Mark adjustment tables along with new Preview window to make it easier to edit adjustment records. +- Added OnMissingCharacter event delegate called when the requested Unicode character is missing from the font asset. +- Fixed Argument Null Exception when attempting to create a font asset via the Create Context menu prior to importing the TMP Essential Resources. Case #1290444 +- Changes to Sorting Layer ID and Sorting Order of <TextMeshPro> will now be reflected on any potential sub text objects. +- Replaced Word Wrapping in the text component inspector by Text Wrapping Mode to introduce new text wrapping options that control whether whitespaces are preserved or ignored at the end of a line. +- Input Field OnSubmit event will no longer be invoked on focus change or edit cancellation. OnEndEdit will continue to be invoked on submit, focus change or cancellation of edit. +- Added support for <SHY> tag which is replaced internally by a soft hyphen or \u00AD. +- Fixed potential text parsing issue when using markup tags without using quotes to encapsulate their values or attribute values. Case #1316658. +- Revised the textInfo property to return a new TMP_TextInfo instance instead of null when the text component has not yet been awaken. Case #1318194. +- Added new property to TMP Settings to control whether or not sub text objects will be visible in the scene hierarchy. +- Added new "Clear Dynamic Data On Build" property to TMP Settings which determines if the "clearDynamicDataOnBuild" font asset property will be set to true or false on newly created dynamic font assets. +- Added fallback character lookup caching to improve subsequent lookups of these characters coming from font assets assigned as local or global fallbacks. +- Added support for Dynamic OS Font Assets. This new dynamic font asset type will enable users to use fonts present on target platforms and devices. See the following video for more details. +- Changed text geometry UV layout which was necessary to support SRP. This change may require updating any code dealing with text geometry modifications such as the VertexColorCycler.cs script included in the TMP Examples & Extras. +

The new UV layout is as follows: + - UV0 has changed from a Vector2 to Vector4. The "x" and "y" properties continue to contain the UV coordinates of the glyph in the font asset atlas texture. The "z" property is reserved for future use and "w" contains the SDF Scale. + - UV2 contains the UV coordinates for the font face and outline texture. Previously, the UV coordinates for the face and outline texture was encoded in UV2.x. +- Added Multi Select support to TMP Dropdown. +- Fixed issue where the <mark> tag color may be incorrect based on line breaking location. See [forum post](https://forum.unity.com/threads/rich-text-tags-dont-apply-to-space-characters-is-there-a-way.1103887/) for details. +- Fixed glyph positional adjustment (kerning) incorrectly being applied to some characters at the end of a line. +- Fixed incorrect text alignment when using <cspace> tag. Case 1333571 +- Fixed potential NullReferenceException in TMP Input Field in SendTouchScreenKeyboardStatusChanged(). See [forum post](https://forum.unity.com/threads/nullreferenceexception-at-tmpro-tmp_inputfield-sendtouchscreenkeyboardstatuschanged.1109678/) for details. +- Obsoleted the SetText(string, bool) function since the text in the Text Input Box is always synced now. Re-added the SetText(string) function. See [forum post](https://forum.unity.com/threads/updating-unity-broke-tmpro.1110794/#post-7183375) for details. +- Revised GetPreferredValues(string, float, float) where it will now return the width of the longest line of text given the width restrictions. See [forum post](https://forum.unity.com/threads/question-on-getpreferredvalues-and-text-wrapping.564970/#post-7189120) for details. +- Fixed GetPreferredValues() potentially returning incorrect values. See [forum post](https://forum.unity.com/threads/question-on-getpreferredvalues-and-text-wrapping.564970/#post-7189120) for details. +- Fixed potential sub text object shader mismatch when changing the shader of the text object. +- Added isAlert property to the TMP Input Field to control if the TouchScreenKeyboard is opened in alert mode. See [forum post](https://forum.unity.com/threads/tmp_inputfield-dark-keyboard-option.698900/#post-7126367) for details. +- Added new property "shouldActivateOnSelect" to the TMP Input Field which determines if the input field will be activated automatically when selected. +- Fixed potential text rendering issue due to incorrect "Additional Shader Channels" when using nested canvases. Case #1337742 +- Newly created sprite assets will now be given a unique name to prevent potential AssetDatabase issue. Case #1345123 +- Fixed potential IndexOutOfRangeException in the Input Field when the text height exceeds the height of the Text Area in conjunction with using text overflow mode Ellipsis or Truncate. Case #1341172 +- Fixed <mark> tag not rendering correctly when using one of the SSD shaders. See [forum post](https://forum.unity.com/threads/mobile-tmpro-support-for-mark-mark.1132414/#post-7335727) for details. +- Fixed potential culling issue that was resulting in the culling being delayed by one frame. Case #1335854 +- Vertex colors of the <TextMeshPro> component will be converted to linear space when Project Settings are set to Linear color space. Case #1349920 +- Fixed potential TouchScreenKeyboard handling issue in the TMP Input Field with UWP. Case #1337129 +- Revised handling of <CR><LF> in the Input Field text where these characters will be treated as one when using Left, Right, Backspace and Del keys when "Allow Rich Text Editing" property is disabled. See [forum post](https://forum.unity.com/threads/pasted-r-n-arent-removed-and-break-input-fields.1139056/#post-7397879) for details. +- Added new property "keepTextSelectionVisible" to the Input Field which maintains the text selection visible when selecting other Input Fields or UI Elements. See [forum post](https://forum.unity.com/threads/input-field-selection-disappear-when-another-ui-item-is-activated.1145375/) for details. +- Fixed potential Input Field text area viewport vertical alignment issue when using a vertical scrollbar and when the child text object's vertical margins are not zero. Case #1353535 +- Revised handling of SDF Scale updates where the SDF Scale will now only be updated when the lossy scale of the text object changes by more than 20%. Case #1352120 + ## [3.0.6] - 2021-04-23 -## [2.1.6] -## [1.5.6] +## [2.1.6] - 2021-04-23 +## [1.5.6] - 2021-04-23 ### Changes -- Added compiler conditional to exclude reference to PS5 in Unity 2019.4.22f1 or older and similar for Unity 2020.2.2f1 or older. +- Removed the use of Out Variable Declarations since it is not support in C# 6.0 and causing internal testing issue on UWP. ## [3.0.5] - 2021-04-09 -## [2.1.5] -## [1.5.5] +## [2.1.5] - 2021-04-09 +## [1.5.5] - 2021-04-09 ### Changes - Added compiler conditional to address error related to missing RectMask2D padding property which was added in Unity 2019.4.12f1. See [forum post](https://forum.unity.com/threads/update-textmesh-pro-to-latest-in-2019-4.945332/#post-6906851) for details. - Fixed GetPreferredValues(string text) and GetPreferredValues(string text, float width, float height) incorrectly changing the text. See [forum post](https://forum.unity.com/threads/preferred-width-height-sometimes-0.980022/#post-6991058) for details. @@ -22,8 +67,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Added support for PS4 and PS5 to TMP Input Field. ## [3.0.4] - 2021-02-19 -## [2.1.4] -## [1.5.4] +## [2.1.4] - 2021-02-19 +## [1.5.4] - 2021-02-19 ### Changes - Improved sprite tag anim functionality to take into consideration the sprite character and glyph scale. Case #1309707 - Improved Ellipsis character insertion handling to prevent potential issues when the Ellipsis glyph ascender and descender exceed those of the primary font asset. See [forum post](https://forum.unity.com/threads/ellipsis-exception.995680/#post-6472790) for details. @@ -54,8 +99,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed an issue with Text Overflow Linked mode where text would not flow correctly from one component to the other when the last character present at the break point was a linefeed "\n" or vertical tab "\v". See [forum post](https://forum.unity.com/threads/odd-line-break-behavior-in-text-with-overflow-linked.1056821/) for details. ## [3.0.3] - 2020-10-27 -## [2.1.3] -## [1.5.3] +## [2.1.3] - 2020-10-27 +## [1.5.3] - 2020-10-27 ### Changes - Fixed potential null reference exception in the Input Field that can occur as a result of using a workflow that involves enabling and disabling Canvases. See [forum post](https://forum.unity.com/threads/tmp_inputfield-generatecaret-m_textcomponent-canvas-exception.940659/) for details. - Fixed potential Invalid AssetDatabase path warning that can be issued when assets are imported from outside the project. See [forum post](https://forum.unity.com/threads/textmesh-pro-invalid-assetdatabase-path-use-path-relative-to-the-project-folder.955731/) for details. @@ -86,8 +131,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed minor UI cosmetic issue in the Sprite Asset Sprite Glyph Table inspector. Case #1285022 ## [3.0.1] - 2020-07-26 -## [2.1.1] -## [1.5.1] +## [2.1.1] - 2020-07-26 +## [1.5.1] - 2020-07-26 ### Changes - Addressed compiler warning related to the new virtual event OnPreRenderText. - Added one additional layer of missing character search where in the even the missing glyph character \u0000 or space character \u0020 is not available in any font asset or potential fallbacks, the End of Text (ETX) \u0003 will be used instead. @@ -97,8 +142,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed potential issue when using multiple <font> tag in the same text object where these referencing several font assets derived from the same font file. Since their Default Material all have the same name, this was causing an issue in the Material Reference Manager. See [forum post](https://forum.unity.com/threads/argumentexception-on-v2-1-0-unity-2019-4-4f1-identified-bug.934789/) for details. Case #1264596. ## [3.0.0] - 2020-06-30 -## [2.1.0] -## [1.5.0] +## [2.1.0] - 2020-06-30 +## [1.5.0] - 2020-06-30 ### Changes - Added support to control if a text object is Maskable and affected by UI Mask components. The new setting is found in the Extra Settings section of the <TextMeshProUGUI> component inspector. - Fixed potential Null Reference Exception when trying to add characters and / or glyphs to a font asset via scripting and before it has been initialized or ReadFontAssetDefinition() has been called. @@ -114,8 +159,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - It will no longer be possible to create Editor Presets for the TMP_FontAsset, TMP_SpriteAsset, TMP_StyleSheet, TMP_ColorGradient and TMP_Settings as these are persistent and runtime assets. Case #1251229 ## [3.0.0-preview.14] - 2020-06-08 -## [2.1.0-preview.14] -## [1.5.0-preview.14] +## [2.1.0-preview.14] - 2020-06-08 +## [1.5.0-preview.14] - 2020-06-08 ### Changes - Fixed sprite character and sprite glyph scale not being reflected in the text layout. See [forum post](https://forum.unity.com/threads/glyph-scale-dont-change-line-height.898817/) for details. - Fixed potential null reference exception in the CrossFadeColor or CrossFadeAlpha functions. See [forum post](https://forum.unity.com/threads/version-1-5-0-2-1-0-3-0-0-preview-13-now-available-for-testing.753587/page-4#post-5885075) for details. @@ -125,8 +170,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed InvalidOperationException that could occur when changing text Overflow linked components via code. Case #1251283 ## [3.0.0-preview.13] - 2020-05-22 -## [2.1.0-preview.13] -## [1.5.0-preview.13] +## [2.1.0-preview.13] - 2020-05-22 +## [1.5.0-preview.13] - 2020-05-22 ### Changes - Fixed potential issue where the Font Asset Creator could get stuck in the packing phase of the atlas generation process. See [forum post](https://forum.unity.com/threads/font-asset-creator-stuck-at-packing-glyphs-pass-8.811863/) for details. - Fixed issue potentially affecting text layout as a result of the width of the RectTransform being incorrectly reported. See [forum post](https://forum.unity.com/threads/textmesh-pro-forcemeshupdate-true-not-working-when-object-inactive.524507/#post-5798515) for details. @@ -134,8 +179,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed culling issue where lossy scale was not considered in the determination of the bounds of the text geometry. ## [3.0.0-preview.12] - 2020-05-09 -## [2.1.0-preview.12] -## [1.5.0-preview.12] +## [2.1.0-preview.12] - 2020-05-09 +## [1.5.0-preview.12] - 2020-05-09 ### Changes - Added synchronization of the RaycastTarget property of the parent <TextMeshProUGUI> with potential child sub text objects. Case #1240784 - Fixed Font Asset Bold Spacing adjustment scaling based on the text object point size instead of current point size. Case #1241132 @@ -149,15 +194,15 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed NullReferenceException when setting the Atlas Texture to None in the Debug Settings of the Material Inspector of a text object. Case #1245104 ## [3.0.0-preview.11] - 2020-04-22 -## [2.1.0-preview.11] -## [1.5.0-preview.11] +## [2.1.0-preview.11] - 2020-04-22 +## [1.5.0-preview.11] - 2020-04-22 ### Changes - Fixed incorrect culling of text object by RectMask2D component when the parent Canvas Render Mode is set to Screen Space - Camera or World Space. Case #1240595 - Added special handling to ForceMeshUpdate() when the parent Canvas is disabled. ## [3.0.0-preview.10] - 2020-04-21 -## [2.1.0-preview.10] -## [1.5.0-preview.10] +## [2.1.0-preview.10] - 2020-04-21 +## [1.5.0-preview.10] - 2020-04-21 ### Changes - Revised caching of Preferred Width and Height to further reduce the amount of time it has to be recomputed when using a complex structure of Layout components. - Fixed potential issue when using Text Overflow Ellipsis and Truncate modes when the text contains characters using superscript, subscript or using the <voffset> tag. @@ -184,8 +229,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed mouse cursor flickering when hovering the Text Input Box of a text prefab with RTL enabled. Case #1206395 ## [3.0.0-preview.8] - 2020-03-14 -## [2.1.0-preview.8] -## [1.5.0-preview.8] +## [2.1.0-preview.8] - 2020-03-14 +## [1.5.0-preview.8] - 2020-03-14 ### Changes - Fixed a minor issue where the preferred width of a text object can be incorrect when using multiple font assets, fallbacks and sprites in the same line of text. - Added Alpha Fade Speed property to the TMP_DropDown inspector. @@ -201,8 +246,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fix incorrect material reference when current font asset is not the primary or a fallback that is missing a character which is present in the primary font asset. ## [3.0.0-preview.7] - 2020-03-07 -## [2.1.0-preview.7] -## [1.5.0-preview.7] +## [2.1.0-preview.7] - 2020-03-07 +## [1.5.0-preview.7] - 2020-03-07 ### Changes - Reverted recent change to the TMP_SubMeshUI OnDisable() function that could result in a Missing Reference Exception in the GraphicRaycaster.cs script. See the following [forum post](https://forum.unity.com/threads/version-1-5-0-2-1-0-preview-5-now-available-for-testing.753587/page-2#post-5523412). - Fixed glyph drawing issue in the Font Asset Inspector Glyph Adjustment Table when Multi Atlas Texture is enabled and the glyph is not located in the main atlas texture or at atlasTextures[0]. @@ -218,8 +263,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - As requested by a few users, TMP_FontAsset.faceInfo setter is now public instead of internal. ## [3.0.0-preview.5] - 2020-02-25 -## [2.1.0-preview.5] -## [1.5.0-preview.5] +## [2.1.0-preview.5] - 2020-02-25 +## [1.5.0-preview.5] - 2020-02-25 ### Changes - Revised SetText function formatting options to including ability to specify padding for integral part of the value. Revised format is as follows: {Arg Index:Integral Padding.Decimal Precision} Example: TMP_Text.SetText("Value = {0:000.00}", 10.375f); result in "Value = 010.38". - Fixed issue where text objects isTextObjectScaleStatic property would be ignored when OnCanvasHierarchyChanged() is called. @@ -232,8 +277,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Text object InternalUpdate() used to handle potential scale changes of text objects now uses willRenderCanvases event instead of onPreCull. This avoids a potential one frame delay in updating of objects and no impact on objects. Case #1216007 ## [3.0.0-preview.4] - 2020-01-31 -## [2.1.0-preview.4] -## [1.5.0-preview.4] +## [2.1.0-preview.4] - 2020-01-31 +## [1.5.0-preview.4] - 2020-01-31 ### Changes - Fixed Input Field issue where scrolling events could prevent OnEndEdit event from firing. See [forum post](https://forum.unity.com/threads/mouse-wheel-on-multiline-input-field-with-no-scrollbar-hangs-input-field-stops-event-firing.794607/) for details. - Improved Input Field handling of Vertical Scrollbar in conjunction with the ResetOnDeActivation property. Using the Vertical Scrollbar no longer triggers OnEndEdit event. @@ -249,8 +294,8 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Added public ITextPreprocessor textPreprocessor; property to allow setting the text preprocessor for a given text component. ## [3.0.0-preview.3] - 2019-12-16 -## [2.1.0-preview.3] -## [1.5.0-preview.3] +## [2.1.0-preview.3] - 2019-12-16 +## [1.5.0-preview.3] - 2019-12-16 ### Changes - Fixed potential issue with TMP Dropdown where calling Show() and Hide() in very short interval could result in additional Blockers. Case #1194114 - Fixed potential issues that could occur when upgrading to version 1.5.0-preview.2 or newer of the TMP package without also updating the TMP Essential Resources in the project. @@ -274,7 +319,7 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Optimization to ensure the TMP Update Manager only rebuilds text objects once per frame regardless of the number of cameras in the scene. ## [2.1.0-preview.2] - 2019-10-30 -## [1.5.0-preview.2] +## [1.5.0-preview.2] - 2019-10-30 ### Changes - Fixed Input Field issue when Read Only flag is set preventing the initial setting of the text. Also fixed Read Only flag not being respected when using IME input. - Fixed potential infinite loop when using text overflow mode ScrollRect. See Case #1188867 @@ -299,7 +344,7 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Fixed potential Null Reference Exception in the Editor when assigning new font asset to disabled game object when no previous font asset was assigned. ## [2.1.0-preview.1] - 2019-09-30 -## [1.5.0-preview.1] +## [1.5.0-preview.1] - 2019-09-30 ### Changes - Fixed an issue when using Overflow Ellipsis mode where the Ellipsis character would not be displayed correctly when the preceding character is a sprite. - Added the ability to define the Resource path for Style Sheets in the TMP Settings. diff --git a/Editor Resources/Shaders/TMP_SDF_SSD.cginc b/Editor Resources/Shaders/TMP_SDF_SSD.cginc index 0f587bd..b16f269 100644 --- a/Editor Resources/Shaders/TMP_SDF_SSD.cginc +++ b/Editor Resources/Shaders/TMP_SDF_SSD.cginc @@ -3,7 +3,7 @@ float4 position : POSITION; float3 normal : NORMAL; float4 color : COLOR; - float2 texcoord0 : TEXCOORD0; + float4 texcoord0 : TEXCOORD0; float2 texcoord1 : TEXCOORD1; }; @@ -34,7 +34,7 @@ pixel_t VertShader(vertex_t input) { pixel_t output; - float bold = step(input.texcoord1.y, 0); + float bold = step(input.texcoord0.w, 0); float4 vert = input.position; vert.x += _VertexOffsetX; diff --git a/Package Resources/TMP Essential Resources.unitypackage b/Package Resources/TMP Essential Resources.unitypackage index df07702..6fa68f5 100644 Binary files a/Package Resources/TMP Essential Resources.unitypackage and b/Package Resources/TMP Essential Resources.unitypackage differ diff --git a/Package Resources/TMP Examples & Extras.unitypackage b/Package Resources/TMP Examples & Extras.unitypackage index e0f2c77..91fcf7d 100644 Binary files a/Package Resources/TMP Examples & Extras.unitypackage and b/Package Resources/TMP Examples & Extras.unitypackage differ diff --git a/Package Resources/TMP Shaders.unitypackage b/Package Resources/TMP Shaders.unitypackage new file mode 100644 index 0000000..ad9285a Binary files /dev/null and b/Package Resources/TMP Shaders.unitypackage differ diff --git a/ValidationExceptions.json.meta b/Package Resources/TMP Shaders.unitypackage.meta similarity index 62% rename from ValidationExceptions.json.meta rename to Package Resources/TMP Shaders.unitypackage.meta index 91e0542..f46fe34 100644 --- a/ValidationExceptions.json.meta +++ b/Package Resources/TMP Shaders.unitypackage.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: c5aeade3b36c0b246936762b3673d9bf -TextScriptImporter: +guid: d3549aab78e3f3742af31b3d196f049d +DefaultImporter: externalObjects: {} userData: assetBundleName: diff --git a/Scripts/Editor/DropdownOptionListDrawer.cs b/Scripts/Editor/DropdownOptionListDrawer.cs index 2b7dc85..144085e 100644 --- a/Scripts/Editor/DropdownOptionListDrawer.cs +++ b/Scripts/Editor/DropdownOptionListDrawer.cs @@ -20,7 +20,7 @@ private void Init(SerializedProperty property) m_ReorderableList = new ReorderableList(property.serializedObject, array); m_ReorderableList.drawElementCallback = DrawOptionData; m_ReorderableList.drawHeaderCallback = DrawHeader; - m_ReorderableList.elementHeight += 16; + m_ReorderableList.elementHeight += 40; } public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) @@ -40,14 +40,17 @@ private void DrawOptionData(Rect rect, int index, bool isActive, bool isFocused) SerializedProperty itemData = m_ReorderableList.serializedProperty.GetArrayElementAtIndex(index); SerializedProperty itemText = itemData.FindPropertyRelative("m_Text"); SerializedProperty itemImage = itemData.FindPropertyRelative("m_Image"); + SerializedProperty itemColor = itemData.FindPropertyRelative("m_Color"); RectOffset offset = new RectOffset(0, 0, -1, -3); rect = offset.Add(rect); rect.height = EditorGUIUtility.singleLineHeight; EditorGUI.PropertyField(rect, itemText, GUIContent.none); - rect.y += EditorGUIUtility.singleLineHeight; + rect.y += EditorGUIUtility.singleLineHeight + 2; EditorGUI.PropertyField(rect, itemImage, GUIContent.none); + rect.y += EditorGUIUtility.singleLineHeight + 2; + EditorGUI.PropertyField(rect, itemColor, GUIContent.none); } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) diff --git a/Scripts/Editor/GlyphMetricsPropertyDrawer.cs b/Scripts/Editor/GlyphMetricsPropertyDrawer.cs index bcd25a6..e83441a 100644 --- a/Scripts/Editor/GlyphMetricsPropertyDrawer.cs +++ b/Scripts/Editor/GlyphMetricsPropertyDrawer.cs @@ -8,7 +8,7 @@ namespace TMPro.EditorUtilities { [CustomPropertyDrawer(typeof(GlyphMetrics))] - public class GlyphMetricsPropertyDrawer : PropertyDrawer + internal class GlyphMetricsPropertyDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { diff --git a/Scripts/Editor/HDRP.meta b/Scripts/Editor/HDRP.meta new file mode 100644 index 0000000..4081097 --- /dev/null +++ b/Scripts/Editor/HDRP.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eecc124dc0b994047aac97ccf8c8ed0a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/HDRP/HDRP_LitShaderGUI.cs b/Scripts/Editor/HDRP/HDRP_LitShaderGUI.cs new file mode 100644 index 0000000..eda18d1 --- /dev/null +++ b/Scripts/Editor/HDRP/HDRP_LitShaderGUI.cs @@ -0,0 +1,83 @@ +#if HDRP_7_5_OR_NEWER +using UnityEditor; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEditor.Rendering.HighDefinition; + +// Include material common properties names +using static UnityEngine.Rendering.HighDefinition.HDMaterialProperties; + +namespace TMPro.EditorUtilities +{ + /// + /// Common GUI for Lit ShaderGraphs + /// + internal class HDRP_LitShaderGUI : HDShaderGUI + { + // For surface option shader graph we only want all unlit features but alpha clip and back then front rendering + const SurfaceOptionUIBlock.Features surfaceOptionFeatures = SurfaceOptionUIBlock.Features.Lit + | SurfaceOptionUIBlock.Features.ShowDepthOffsetOnly; + + const EmissionUIBlock.Features emissionFeatures = EmissionUIBlock.Features.All ^ EmissionUIBlock.Features.EnableEmissionForGI; + + MaterialUIBlockList m_UIBlocks = new MaterialUIBlockList + { + new SurfaceOptionUIBlock(MaterialUIBlock.Expandable.Base, features: surfaceOptionFeatures), + new ShaderGraphUIBlock(MaterialUIBlock.Expandable.ShaderGraph), + //new EmissionUIBlock(MaterialUIBlock.Expandable.Emissive), + new AdvancedOptionsUIBlock(MaterialUIBlock.Expandable.Advance, ~AdvancedOptionsUIBlock.Features.SpecularOcclusion) + }; + + protected MaterialUIBlockList uiBlocks => m_UIBlocks; + + /// + /// Implement your custom GUI in this function. To display a UI similar to HDRP shaders, use a MaterialUIBlock. + /// + /// The current material editor. + /// The list of properties the material has. + protected override void OnMaterialGUI(MaterialEditor materialEditor, MaterialProperty[] props) + { + using (var changed = new EditorGUI.ChangeCheckScope()) + { + m_UIBlocks.OnGUI(materialEditor, props); + ApplyKeywordsAndPassesIfNeeded(changed.changed, m_UIBlocks.materials); + } + } + + //const string kUVEmissive = "_UVEmissive"; + + /// + /// Sets up the keywords and passes for a Lit Shader Graph material. + /// + /// The target material. + public static void SetupMaterialKeywordsAndPass(Material material) + { + SynchronizeShaderGraphProperties(material); + + BaseLitGUI.SetupBaseLitKeywords(material); + BaseLitGUI.SetupBaseLitMaterialPass(material); + + bool receiveSSR = false; + if (material.GetSurfaceType() == SurfaceType.Transparent) + receiveSSR = material.HasProperty(kReceivesSSRTransparent) ? material.GetFloat(kReceivesSSRTransparent) != 0 : false; + else + receiveSSR = material.HasProperty(kReceivesSSR) ? material.GetFloat(kReceivesSSR) != 0 : false; + bool useSplitLighting = material.HasProperty(kUseSplitLighting) ? material.GetInt(kUseSplitLighting) != 0: false; + BaseLitGUI.SetupStencil(material, receiveSSR, useSplitLighting); + + if (material.HasProperty(kAddPrecomputedVelocity)) + CoreUtils.SetKeyword(material, "_ADD_PRECOMPUTED_VELOCITY", material.GetInt(kAddPrecomputedVelocity) != 0); + + /*if (material.HasProperty(kUVEmissive) && material.HasProperty(kEmissiveColorMap)) + { + CoreUtils.SetKeyword(material, "_EMISSIVE_MAPPING_PLANAR", ((UVEmissiveMapping)material.GetFloat(kUVEmissive)) == UVEmissiveMapping.Planar && material.GetTexture(kEmissiveColorMap)); + CoreUtils.SetKeyword(material, "_EMISSIVE_MAPPING_TRIPLANAR", ((UVEmissiveMapping)material.GetFloat(kUVEmissive)) == UVEmissiveMapping.Triplanar && material.GetTexture(kEmissiveColorMap)); + CoreUtils.SetKeyword(material, "_EMISSIVE_MAPPING_BASE", ((UVEmissiveMapping)material.GetFloat(kUVEmissive)) == UVEmissiveMapping.SameAsBase && material.GetTexture(kEmissiveColorMap)); + CoreUtils.SetKeyword(material, "_EMISSIVE_COLOR_MAP", material.GetTexture(kEmissiveColorMap)); + }*/ + } + + protected override void SetupMaterialKeywordsAndPassInternal(Material material) => SetupMaterialKeywordsAndPass(material); + } +} +#endif diff --git a/Scripts/Editor/TMPro_FontPlugin.cs.meta b/Scripts/Editor/HDRP/HDRP_LitShaderGUI.cs.meta similarity index 74% rename from Scripts/Editor/TMPro_FontPlugin.cs.meta rename to Scripts/Editor/HDRP/HDRP_LitShaderGUI.cs.meta index 66f3a87..c7bd79c 100644 --- a/Scripts/Editor/TMPro_FontPlugin.cs.meta +++ b/Scripts/Editor/HDRP/HDRP_LitShaderGUI.cs.meta @@ -1,6 +1,7 @@ fileFormatVersion: 2 -guid: 9edc9283e7d6409fab242fe8fb6a822c +guid: bbe5cdecf49c00345827736b4c46fe52 MonoImporter: + externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 diff --git a/Scripts/Editor/HDRP/HDRP_UnlitShaderGUI.cs b/Scripts/Editor/HDRP/HDRP_UnlitShaderGUI.cs new file mode 100644 index 0000000..7443759 --- /dev/null +++ b/Scripts/Editor/HDRP/HDRP_UnlitShaderGUI.cs @@ -0,0 +1,57 @@ +#if HDRP_7_5_OR_NEWER +using UnityEditor; +using UnityEngine; +using UnityEditor.Rendering.HighDefinition; + + +namespace TMPro.EditorUtilities +{ + /// + /// Common GUI for Lit ShaderGraphs + /// + internal class HDRP_UnlitShaderGUI : HDShaderGUI + { + const SurfaceOptionUIBlock.Features surfaceOptionFeatures = SurfaceOptionUIBlock.Features.Unlit; + + MaterialUIBlockList m_UIBlocks = new MaterialUIBlockList + { + new SurfaceOptionUIBlock(MaterialUIBlock.Expandable.Base, features: surfaceOptionFeatures), + new ShaderGraphUIBlock(MaterialUIBlock.Expandable.ShaderGraph, ShaderGraphUIBlock.Features.Unlit), + new AdvancedOptionsUIBlock(MaterialUIBlock.Expandable.Advance, ~AdvancedOptionsUIBlock.Features.SpecularOcclusion) + }; + + /// List of UI Blocks used to render the material inspector. + protected MaterialUIBlockList uiBlocks => m_UIBlocks; + + /// + /// Implement your custom GUI in this function. To display a UI similar to HDRP shaders, use a MaterialUIBlockList. + /// + /// The current material editor. + /// The list of properties the material has. + protected override void OnMaterialGUI(MaterialEditor materialEditor, MaterialProperty[] props) + { + using (var changed = new EditorGUI.ChangeCheckScope()) + { + m_UIBlocks.OnGUI(materialEditor, props); + ApplyKeywordsAndPassesIfNeeded(changed.changed, m_UIBlocks.materials); + } + } + + /// + /// Sets up the keywords and passes for the Unlit Shader Graph material you pass in. + /// + /// The target material. + public static void SetupUnlitKeywordsAndPass(Material material) + { + SynchronizeShaderGraphProperties(material); + UnlitGUI.SetupUnlitMaterialKeywordsAndPass(material); + } + + /// + /// Sets up the keywords and passes for the current selected material. + /// + /// The selected material. + protected override void SetupMaterialKeywordsAndPassInternal(Material material) => SetupUnlitKeywordsAndPass(material); + } +} +#endif diff --git a/Scripts/Editor/HDRP/HDRP_UnlitShaderGUI.cs.meta b/Scripts/Editor/HDRP/HDRP_UnlitShaderGUI.cs.meta new file mode 100644 index 0000000..d000e8c --- /dev/null +++ b/Scripts/Editor/HDRP/HDRP_UnlitShaderGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 93ee4ad131ebc114b8799cb8484b86f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/HDRP/TMP_BaseHDRPLitShaderGUI.cs b/Scripts/Editor/HDRP/TMP_BaseHDRPLitShaderGUI.cs new file mode 100644 index 0000000..b9272e1 --- /dev/null +++ b/Scripts/Editor/HDRP/TMP_BaseHDRPLitShaderGUI.cs @@ -0,0 +1,677 @@ +#if HDRP_7_5_OR_NEWER +using UnityEngine; +using UnityEditor; +using UnityEditor.Rendering.HighDefinition; + + +namespace TMPro.EditorUtilities +{ + /// Base class for TextMesh Pro shader GUIs. + internal abstract class TMP_BaseHDRPLitShaderGUI : HDRP_LitShaderGUI + { + /// Representation of a #pragma shader_feature. + /// It is assumed that the first feature option is for no keyword (underscores). + protected class ShaderFeature + { + public string undoLabel; + + public GUIContent label; + + /// The keyword labels, for display. Include the no-keyword as the first option. + public GUIContent[] keywordLabels; + + /// The shader keywords. Exclude the no-keyword option. + public string[] keywords; + + int m_State; + + public bool Active + { + get { return m_State >= 0; } + } + + public int State + { + get { return m_State; } + } + + public void ReadState(Material material) + { + for (int i = 0; i < keywords.Length; i++) + { + if (material.IsKeywordEnabled(keywords[i])) + { + m_State = i; + return; + } + } + + m_State = -1; + } + + public void SetActive(bool active, Material material) + { + m_State = active ? 0 : -1; + SetStateKeywords(material); + } + + public void DoPopup(MaterialEditor editor, Material material) + { + EditorGUI.BeginChangeCheck(); + int selection = EditorGUILayout.Popup(label, m_State + 1, keywordLabels); + if (EditorGUI.EndChangeCheck()) + { + m_State = selection - 1; + editor.RegisterPropertyChangeUndo(undoLabel); + SetStateKeywords(material); + } + } + + void SetStateKeywords(Material material) + { + for (int i = 0; i < keywords.Length; i++) + { + if (i == m_State) + { + material.EnableKeyword(keywords[i]); + } + else + { + material.DisableKeyword(keywords[i]); + } + } + } + } + + static GUIContent s_TempLabel = new GUIContent(); + + protected static bool s_DebugExtended; + + static int s_UndoRedoCount, s_LastSeenUndoRedoCount; + + static float[][] s_TempFloats = + { + null, new float[1], new float[2], new float[3], new float[4] + }; + + protected static GUIContent[] s_XywhVectorLabels = + { + new GUIContent("X"), + new GUIContent("Y"), + new GUIContent("W", "Width"), + new GUIContent("H", "Height") + }; + + protected static GUIContent[] s_LbrtVectorLabels = + { + new GUIContent("L", "Left"), + new GUIContent("B", "Bottom"), + new GUIContent("R", "Right"), + new GUIContent("T", "Top") + }; + + protected static GUIContent[] s_CullingTypeLabels = + { + new GUIContent("Off"), + new GUIContent("Front"), + new GUIContent("Back") + }; + + static TMP_BaseHDRPLitShaderGUI() + { + // Keep track of how many undo/redo events happened. + Undo.undoRedoPerformed += () => s_UndoRedoCount += 1; + } + + bool m_IsNewGUI = true; + + float m_DragAndDropMinY; + + protected MaterialEditor m_Editor; + + protected Material m_Material; + + protected MaterialProperty[] m_Properties; + + void PrepareGUI() + { + m_IsNewGUI = false; + ShaderUtilities.GetShaderPropertyIDs(); + + // New GUI just got constructed. This happens in response to a selection, + // but also after undo/redo events. + if (s_LastSeenUndoRedoCount != s_UndoRedoCount) + { + // There's been at least one undo/redo since the last time this GUI got constructed. + // Maybe the undo/redo was for this material? Assume that is was. + TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material as Material); + } + + s_LastSeenUndoRedoCount = s_UndoRedoCount; + } + + protected override void OnMaterialGUI(MaterialEditor materialEditor, MaterialProperty[] properties) + { + m_Editor = materialEditor; + m_Material = materialEditor.target as Material; + this.m_Properties = properties; + + if (m_IsNewGUI) + { + PrepareGUI(); + } + + DoDragAndDropBegin(); + EditorGUI.BeginChangeCheck(); + DoGUI(); + if (EditorGUI.EndChangeCheck()) + { + TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material); + } + + DoDragAndDropEnd(); + } + + /// Override this method to create the specific shader GUI. + protected abstract void DoGUI(); + + static string[] s_PanelStateLabel = new string[] { "\t- Click to collapse -", "\t- Click to expand -" }; + + protected bool BeginPanel(string panel, bool expanded) + { + EditorGUI.indentLevel = 0; + + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + Rect r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 18)); + r.x += 20; + r.width += 6; + + bool enabled = GUI.enabled; + GUI.enabled = true; + expanded = TMP_EditorUtility.EditorToggle(r, expanded, new GUIContent(panel), TMP_UIStyleManager.panelTitle); + r.width -= 30; + EditorGUI.LabelField(r, new GUIContent(expanded ? s_PanelStateLabel[0] : s_PanelStateLabel[1]), TMP_UIStyleManager.rightLabel); + GUI.enabled = enabled; + + EditorGUI.indentLevel += 1; + EditorGUI.BeginDisabledGroup(false); + + return expanded; + } + + protected bool BeginPanel(string panel, ShaderFeature feature, bool expanded, bool readState = true) + { + EditorGUI.indentLevel = 0; + + if (readState) + { + feature.ReadState(m_Material); + } + + EditorGUI.BeginChangeCheck(); + + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + GUILayout.BeginHorizontal(); + + Rect r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 20, GUILayout.Width(20f))); + bool active = EditorGUI.Toggle(r, feature.Active); + + if (EditorGUI.EndChangeCheck()) + { + m_Editor.RegisterPropertyChangeUndo(feature.undoLabel); + feature.SetActive(active, m_Material); + } + + r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 18)); + r.width += 6; + + bool enabled = GUI.enabled; + GUI.enabled = true; + expanded = TMP_EditorUtility.EditorToggle(r, expanded, new GUIContent(panel), TMP_UIStyleManager.panelTitle); + r.width -= 10; + EditorGUI.LabelField(r, new GUIContent(expanded ? s_PanelStateLabel[0] : s_PanelStateLabel[1]), TMP_UIStyleManager.rightLabel); + GUI.enabled = enabled; + + GUILayout.EndHorizontal(); + + EditorGUI.indentLevel += 1; + EditorGUI.BeginDisabledGroup(!active); + + return expanded; + } + + protected void EndPanel() + { + EditorGUI.EndDisabledGroup(); + EditorGUI.indentLevel -= 1; + EditorGUILayout.EndVertical(); + } + + MaterialProperty BeginProperty(string name) + { + MaterialProperty property = FindProperty(name, m_Properties); + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = property.hasMixedValue; + m_Editor.BeginAnimatedCheck(Rect.zero, property); + + return property; + } + + bool EndProperty() + { + m_Editor.EndAnimatedCheck(); + EditorGUI.showMixedValue = false; + return EditorGUI.EndChangeCheck(); + } + + protected void DoPopup(string name, string label, GUIContent[] options) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + int index = EditorGUILayout.Popup(s_TempLabel, (int)property.floatValue, options); + if (EndProperty()) + { + property.floatValue = index; + } + } + + protected void DoCubeMap(string name, string label) + { + DoTexture(name, label, typeof(Cubemap)); + } + + protected void DoTexture2D(string name, string label, bool withTilingOffset = false, string[] speedNames = null) + { + DoTexture(name, label, typeof(Texture2D), withTilingOffset, speedNames); + } + + void DoTexture(string name, string label, System.Type type, bool withTilingOffset = false, string[] speedNames = null) + { + float objFieldSize = 60f; + bool smallLayout = EditorGUIUtility.currentViewWidth <= 440f && (withTilingOffset || speedNames != null); + float controlHeight = smallLayout ? objFieldSize * 2 : objFieldSize; + + MaterialProperty property = FindProperty(name, m_Properties); + m_Editor.BeginAnimatedCheck(Rect.zero, property); + + Rect rect = EditorGUILayout.GetControlRect(true, controlHeight); + float totalWidth = rect.width; + rect.width = EditorGUIUtility.labelWidth + objFieldSize; + rect.height = objFieldSize; + s_TempLabel.text = label; + + EditorGUI.BeginChangeCheck(); + Object tex = EditorGUI.ObjectField(rect, s_TempLabel, property.textureValue, type, false); + if (EditorGUI.EndChangeCheck()) + { + property.textureValue = tex as Texture; + } + + float additionalHeight = controlHeight - objFieldSize; + float xOffset = smallLayout ? rect.width - objFieldSize : rect.width; + + rect.y += additionalHeight; + rect.x += xOffset; + rect.width = totalWidth - xOffset; + rect.height = EditorGUIUtility.singleLineHeight; + + if (withTilingOffset) + { + DoTilingOffset(rect, property); + rect.y += (rect.height + 2f) * 2f; + } + + m_Editor.EndAnimatedCheck(); + + if (speedNames != null) + { + DoUVSpeed(rect, speedNames); + } + } + + void DoTilingOffset(Rect rect, MaterialProperty property) + { + float labelWidth = EditorGUIUtility.labelWidth; + int indentLevel = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + EditorGUIUtility.labelWidth = Mathf.Min(40f, rect.width * 0.40f); + + Vector4 vector = property.textureScaleAndOffset; + + bool changed = false; + float[] values = s_TempFloats[2]; + + s_TempLabel.text = "Tiling"; + Rect vectorRect = EditorGUI.PrefixLabel(rect, s_TempLabel); + values[0] = vector.x; + values[1] = vector.y; + + EditorGUI.BeginChangeCheck(); + EditorGUI.MultiFloatField(vectorRect, s_XywhVectorLabels, values); + if (EditorGUI.EndChangeCheck()) + { + vector.x = values[0]; + vector.y = values[1]; + changed = true; + } + + rect.y += rect.height + 2f; + s_TempLabel.text = "Offset"; + vectorRect = EditorGUI.PrefixLabel(rect, s_TempLabel); + values[0] = vector.z; + values[1] = vector.w; + + EditorGUI.BeginChangeCheck(); + EditorGUI.MultiFloatField(vectorRect, s_XywhVectorLabels, values); + if (EditorGUI.EndChangeCheck()) + { + vector.z = values[0]; + vector.w = values[1]; + changed = true; + } + + if (changed) + { + property.textureScaleAndOffset = vector; + } + + EditorGUIUtility.labelWidth = labelWidth; + EditorGUI.indentLevel = indentLevel; + } + + void DoUVSpeed(Rect rect, string[] names) + { + float labelWidth = EditorGUIUtility.labelWidth; + int indentLevel = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + EditorGUIUtility.labelWidth = Mathf.Min(40f, rect.width * 0.40f); + + s_TempLabel.text = "Speed"; + rect = EditorGUI.PrefixLabel(rect, s_TempLabel); + + EditorGUIUtility.labelWidth = 10f; + rect.width = rect.width * 0.5f - 2f; + + if (names.Length == 1) + { + DoFloat2(rect, names[0]); + } + else + { + DoFloat(rect, names[0], "X"); + rect.x += rect.width + 4f; + DoFloat(rect, names[1], "Y"); + } + + EditorGUIUtility.labelWidth = labelWidth; + EditorGUI.indentLevel = indentLevel; + } + + protected void DoToggle(string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + bool value = EditorGUILayout.Toggle(s_TempLabel, property.floatValue == 1f); + if (EndProperty()) + { + property.floatValue = value ? 1f : 0f; + } + } + + protected void DoFloat(string name, string label) + { + MaterialProperty property = BeginProperty(name); + Rect rect = EditorGUILayout.GetControlRect(); + rect.width = EditorGUIUtility.labelWidth + 55f; + s_TempLabel.text = label; + float value = EditorGUI.FloatField(rect, s_TempLabel, property.floatValue); + if (EndProperty()) + { + property.floatValue = value; + } + } + + protected void DoColor(string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + Color value = EditorGUI.ColorField(EditorGUILayout.GetControlRect(), s_TempLabel, property.colorValue, false, true, true); + if (EndProperty()) + { + property.colorValue = value; + } + } + + void DoFloat(Rect rect, string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + float value = EditorGUI.FloatField(rect, s_TempLabel, property.floatValue); + if (EndProperty()) + { + property.floatValue = value; + } + } + + void DoFloat2(Rect rect, string name) + { + MaterialProperty property = BeginProperty(name); + + float x = EditorGUI.FloatField(rect, "X", property.vectorValue.x); + rect.x += rect.width + 4f; + float y = EditorGUI.FloatField(rect, "Y", property.vectorValue.y); + + if (EndProperty()) + { + property.vectorValue = new Vector2(x, y); + } + } + + protected void DoOffset(string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + Vector2 value = EditorGUI.Vector2Field(EditorGUILayout.GetControlRect(), s_TempLabel, property.vectorValue); + if (EndProperty()) + { + property.vectorValue = value; + } + } + + protected void DoSlider(string name, string label) + { + MaterialProperty property = BeginProperty(name); + Vector2 range = property.rangeLimits; + s_TempLabel.text = label; + float value = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, property.floatValue, range.x, range.y); + if (EndProperty()) + { + property.floatValue = value; + } + } + + protected void DoSlider(string name, Vector2 range, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + float value = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, property.floatValue, range.x, range.y); + if (EndProperty()) + { + property.floatValue = value; + } + } + + protected void DoSlider(string propertyName, string propertyField, string label) + { + MaterialProperty property = BeginProperty(propertyName); + Vector2 range = property.rangeLimits; + s_TempLabel.text = label; + + Vector4 value = property.vectorValue; + + switch (propertyField) + { + case "X": + value.x = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.x, range.x, range.y); + break; + case "Y": + value.y = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.y, range.x, range.y); + break; + case "Z": + value.z = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.z, range.x, range.y); + break; + case "W": + value.w = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.w, range.x, range.y); + break; + } + + if (EndProperty()) + { + property.vectorValue = value; + } + } + + protected void DoSlider(string propertyName, string propertyField, Vector2 range, string label) + { + MaterialProperty property = BeginProperty(propertyName); + s_TempLabel.text = label; + + Vector4 value = property.vectorValue; + + switch (propertyField) + { + case "X": + value.x = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.x, range.x, range.y); + break; + case "Y": + value.y = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.y, range.x, range.y); + break; + case "Z": + value.z = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.z, range.x, range.y); + break; + case "W": + value.w = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.w, range.x, range.y); + break; + } + + if (EndProperty()) + { + property.vectorValue = value; + } + } + + protected void DoVector2(string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + Vector4 value = EditorGUILayout.Vector3Field(s_TempLabel, property.vectorValue); + if (EndProperty()) + { + property.vectorValue = value; + } + } + + protected void DoVector3(string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + Vector4 value = EditorGUILayout.Vector3Field(s_TempLabel, property.vectorValue); + if (EndProperty()) + { + property.vectorValue = value; + } + } + + protected void DoVector(string name, string label, GUIContent[] subLabels) + { + MaterialProperty property = BeginProperty(name); + Rect rect = EditorGUILayout.GetControlRect(); + s_TempLabel.text = label; + rect = EditorGUI.PrefixLabel(rect, s_TempLabel); + Vector4 vector = property.vectorValue; + + float[] values = s_TempFloats[subLabels.Length]; + for (int i = 0; i < subLabels.Length; i++) + { + values[i] = vector[i]; + } + + EditorGUI.MultiFloatField(rect, subLabels, values); + if (EndProperty()) + { + for (int i = 0; i < subLabels.Length; i++) + { + vector[i] = values[i]; + } + + property.vectorValue = vector; + } + } + + void DoDragAndDropBegin() + { + m_DragAndDropMinY = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)).y; + } + + void DoDragAndDropEnd() + { + Rect rect = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)); + Event evt = Event.current; + if (evt.type == EventType.DragUpdated) + { + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + evt.Use(); + } + else if ( + evt.type == EventType.DragPerform && + Rect.MinMaxRect(rect.xMin, m_DragAndDropMinY, rect.xMax, rect.yMax).Contains(evt.mousePosition) + ) + { + DragAndDrop.AcceptDrag(); + evt.Use(); + Material droppedMaterial = DragAndDrop.objectReferences[0] as Material; + if (droppedMaterial && droppedMaterial != m_Material) + { + PerformDrop(droppedMaterial); + } + } + } + + void PerformDrop(Material droppedMaterial) + { + Texture droppedTex = droppedMaterial.GetTexture(ShaderUtilities.ID_MainTex); + if (!droppedTex) + { + return; + } + + Texture currentTex = m_Material.GetTexture(ShaderUtilities.ID_MainTex); + TMP_FontAsset requiredFontAsset = null; + if (droppedTex != currentTex) + { + requiredFontAsset = TMP_EditorUtility.FindMatchingFontAsset(droppedMaterial); + if (!requiredFontAsset) + { + return; + } + } + + foreach (GameObject o in Selection.gameObjects) + { + if (requiredFontAsset) + { + TMP_Text textComponent = o.GetComponent(); + if (textComponent) + { + Undo.RecordObject(textComponent, "Font Asset Change"); + textComponent.font = requiredFontAsset; + } + } + + TMPro_EventManager.ON_DRAG_AND_DROP_MATERIAL_CHANGED(o, m_Material, droppedMaterial); + EditorUtility.SetDirty(o); + } + } + } +} +#endif diff --git a/Scripts/Editor/HDRP/TMP_BaseHDRPLitShaderGUI.cs.meta b/Scripts/Editor/HDRP/TMP_BaseHDRPLitShaderGUI.cs.meta new file mode 100644 index 0000000..6520fed --- /dev/null +++ b/Scripts/Editor/HDRP/TMP_BaseHDRPLitShaderGUI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e3795795b029fde4395e6953ce72b5a6 +timeCreated: 1469844810 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/HDRP/TMP_BaseHDRPUnlitShaderGUI.cs b/Scripts/Editor/HDRP/TMP_BaseHDRPUnlitShaderGUI.cs new file mode 100644 index 0000000..e1f3e5b --- /dev/null +++ b/Scripts/Editor/HDRP/TMP_BaseHDRPUnlitShaderGUI.cs @@ -0,0 +1,677 @@ +#if HDRP_7_5_OR_NEWER +using UnityEngine; +using UnityEditor; +using UnityEditor.Rendering.HighDefinition; + + +namespace TMPro.EditorUtilities +{ + /// Base class for TextMesh Pro shader GUIs. + internal abstract class TMP_BaseHDRPUnlitShaderGUI : HDRP_UnlitShaderGUI + { + /// Representation of a #pragma shader_feature. + /// It is assumed that the first feature option is for no keyword (underscores). + protected class ShaderFeature + { + public string undoLabel; + + public GUIContent label; + + /// The keyword labels, for display. Include the no-keyword as the first option. + public GUIContent[] keywordLabels; + + /// The shader keywords. Exclude the no-keyword option. + public string[] keywords; + + int m_State; + + public bool Active + { + get { return m_State >= 0; } + } + + public int State + { + get { return m_State; } + } + + public void ReadState(Material material) + { + for (int i = 0; i < keywords.Length; i++) + { + if (material.IsKeywordEnabled(keywords[i])) + { + m_State = i; + return; + } + } + + m_State = -1; + } + + public void SetActive(bool active, Material material) + { + m_State = active ? 0 : -1; + SetStateKeywords(material); + } + + public void DoPopup(MaterialEditor editor, Material material) + { + EditorGUI.BeginChangeCheck(); + int selection = EditorGUILayout.Popup(label, m_State + 1, keywordLabels); + if (EditorGUI.EndChangeCheck()) + { + m_State = selection - 1; + editor.RegisterPropertyChangeUndo(undoLabel); + SetStateKeywords(material); + } + } + + void SetStateKeywords(Material material) + { + for (int i = 0; i < keywords.Length; i++) + { + if (i == m_State) + { + material.EnableKeyword(keywords[i]); + } + else + { + material.DisableKeyword(keywords[i]); + } + } + } + } + + static GUIContent s_TempLabel = new GUIContent(); + + protected static bool s_DebugExtended; + + static int s_UndoRedoCount, s_LastSeenUndoRedoCount; + + static float[][] s_TempFloats = + { + null, new float[1], new float[2], new float[3], new float[4] + }; + + protected static GUIContent[] s_XywhVectorLabels = + { + new GUIContent("X"), + new GUIContent("Y"), + new GUIContent("W", "Width"), + new GUIContent("H", "Height") + }; + + protected static GUIContent[] s_LbrtVectorLabels = + { + new GUIContent("L", "Left"), + new GUIContent("B", "Bottom"), + new GUIContent("R", "Right"), + new GUIContent("T", "Top") + }; + + protected static GUIContent[] s_CullingTypeLabels = + { + new GUIContent("Off"), + new GUIContent("Front"), + new GUIContent("Back") + }; + + static TMP_BaseHDRPUnlitShaderGUI() + { + // Keep track of how many undo/redo events happened. + Undo.undoRedoPerformed += () => s_UndoRedoCount += 1; + } + + bool m_IsNewGUI = true; + + float m_DragAndDropMinY; + + protected MaterialEditor m_Editor; + + protected Material m_Material; + + protected MaterialProperty[] m_Properties; + + void PrepareGUI() + { + m_IsNewGUI = false; + ShaderUtilities.GetShaderPropertyIDs(); + + // New GUI just got constructed. This happens in response to a selection, + // but also after undo/redo events. + if (s_LastSeenUndoRedoCount != s_UndoRedoCount) + { + // There's been at least one undo/redo since the last time this GUI got constructed. + // Maybe the undo/redo was for this material? Assume that is was. + TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material as Material); + } + + s_LastSeenUndoRedoCount = s_UndoRedoCount; + } + + protected override void OnMaterialGUI(MaterialEditor materialEditor, MaterialProperty[] properties) + { + m_Editor = materialEditor; + m_Material = materialEditor.target as Material; + this.m_Properties = properties; + + if (m_IsNewGUI) + { + PrepareGUI(); + } + + DoDragAndDropBegin(); + EditorGUI.BeginChangeCheck(); + DoGUI(); + if (EditorGUI.EndChangeCheck()) + { + TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material); + } + + DoDragAndDropEnd(); + } + + /// Override this method to create the specific shader GUI. + protected abstract void DoGUI(); + + static string[] s_PanelStateLabel = new string[] { "\t- Click to collapse -", "\t- Click to expand -" }; + + protected bool BeginPanel(string panel, bool expanded) + { + EditorGUI.indentLevel = 0; + + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + Rect r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 18)); + r.x += 20; + r.width += 6; + + bool enabled = GUI.enabled; + GUI.enabled = true; + expanded = TMP_EditorUtility.EditorToggle(r, expanded, new GUIContent(panel), TMP_UIStyleManager.panelTitle); + r.width -= 30; + EditorGUI.LabelField(r, new GUIContent(expanded ? s_PanelStateLabel[0] : s_PanelStateLabel[1]), TMP_UIStyleManager.rightLabel); + GUI.enabled = enabled; + + EditorGUI.indentLevel += 1; + EditorGUI.BeginDisabledGroup(false); + + return expanded; + } + + protected bool BeginPanel(string panel, ShaderFeature feature, bool expanded, bool readState = true) + { + EditorGUI.indentLevel = 0; + + if (readState) + { + feature.ReadState(m_Material); + } + + EditorGUI.BeginChangeCheck(); + + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + GUILayout.BeginHorizontal(); + + Rect r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 20, GUILayout.Width(20f))); + bool active = EditorGUI.Toggle(r, feature.Active); + + if (EditorGUI.EndChangeCheck()) + { + m_Editor.RegisterPropertyChangeUndo(feature.undoLabel); + feature.SetActive(active, m_Material); + } + + r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 18)); + r.width += 6; + + bool enabled = GUI.enabled; + GUI.enabled = true; + expanded = TMP_EditorUtility.EditorToggle(r, expanded, new GUIContent(panel), TMP_UIStyleManager.panelTitle); + r.width -= 10; + EditorGUI.LabelField(r, new GUIContent(expanded ? s_PanelStateLabel[0] : s_PanelStateLabel[1]), TMP_UIStyleManager.rightLabel); + GUI.enabled = enabled; + + GUILayout.EndHorizontal(); + + EditorGUI.indentLevel += 1; + EditorGUI.BeginDisabledGroup(!active); + + return expanded; + } + + protected void EndPanel() + { + EditorGUI.EndDisabledGroup(); + EditorGUI.indentLevel -= 1; + EditorGUILayout.EndVertical(); + } + + MaterialProperty BeginProperty(string name) + { + MaterialProperty property = FindProperty(name, m_Properties); + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = property.hasMixedValue; + m_Editor.BeginAnimatedCheck(Rect.zero, property); + + return property; + } + + bool EndProperty() + { + m_Editor.EndAnimatedCheck(); + EditorGUI.showMixedValue = false; + return EditorGUI.EndChangeCheck(); + } + + protected void DoPopup(string name, string label, GUIContent[] options) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + int index = EditorGUILayout.Popup(s_TempLabel, (int)property.floatValue, options); + if (EndProperty()) + { + property.floatValue = index; + } + } + + protected void DoCubeMap(string name, string label) + { + DoTexture(name, label, typeof(Cubemap)); + } + + protected void DoTexture2D(string name, string label, bool withTilingOffset = false, string[] speedNames = null) + { + DoTexture(name, label, typeof(Texture2D), withTilingOffset, speedNames); + } + + void DoTexture(string name, string label, System.Type type, bool withTilingOffset = false, string[] speedNames = null) + { + float objFieldSize = 60f; + bool smallLayout = EditorGUIUtility.currentViewWidth <= 440f && (withTilingOffset || speedNames != null); + float controlHeight = smallLayout ? objFieldSize * 2 : objFieldSize; + + MaterialProperty property = FindProperty(name, m_Properties); + m_Editor.BeginAnimatedCheck(Rect.zero, property); + + Rect rect = EditorGUILayout.GetControlRect(true, controlHeight); + float totalWidth = rect.width; + rect.width = EditorGUIUtility.labelWidth + objFieldSize; + rect.height = objFieldSize; + s_TempLabel.text = label; + + EditorGUI.BeginChangeCheck(); + Object tex = EditorGUI.ObjectField(rect, s_TempLabel, property.textureValue, type, false); + if (EditorGUI.EndChangeCheck()) + { + property.textureValue = tex as Texture; + } + + float additionalHeight = controlHeight - objFieldSize; + float xOffset = smallLayout ? rect.width - objFieldSize : rect.width; + + rect.y += additionalHeight; + rect.x += xOffset; + rect.width = totalWidth - xOffset; + rect.height = EditorGUIUtility.singleLineHeight; + + if (withTilingOffset) + { + DoTilingOffset(rect, property); + rect.y += (rect.height + 2f) * 2f; + } + + m_Editor.EndAnimatedCheck(); + + if (speedNames != null) + { + DoUVSpeed(rect, speedNames); + } + } + + void DoTilingOffset(Rect rect, MaterialProperty property) + { + float labelWidth = EditorGUIUtility.labelWidth; + int indentLevel = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + EditorGUIUtility.labelWidth = Mathf.Min(40f, rect.width * 0.40f); + + Vector4 vector = property.textureScaleAndOffset; + + bool changed = false; + float[] values = s_TempFloats[2]; + + s_TempLabel.text = "Tiling"; + Rect vectorRect = EditorGUI.PrefixLabel(rect, s_TempLabel); + values[0] = vector.x; + values[1] = vector.y; + + EditorGUI.BeginChangeCheck(); + EditorGUI.MultiFloatField(vectorRect, s_XywhVectorLabels, values); + if (EditorGUI.EndChangeCheck()) + { + vector.x = values[0]; + vector.y = values[1]; + changed = true; + } + + rect.y += rect.height + 2f; + s_TempLabel.text = "Offset"; + vectorRect = EditorGUI.PrefixLabel(rect, s_TempLabel); + values[0] = vector.z; + values[1] = vector.w; + + EditorGUI.BeginChangeCheck(); + EditorGUI.MultiFloatField(vectorRect, s_XywhVectorLabels, values); + if (EditorGUI.EndChangeCheck()) + { + vector.z = values[0]; + vector.w = values[1]; + changed = true; + } + + if (changed) + { + property.textureScaleAndOffset = vector; + } + + EditorGUIUtility.labelWidth = labelWidth; + EditorGUI.indentLevel = indentLevel; + } + + void DoUVSpeed(Rect rect, string[] names) + { + float labelWidth = EditorGUIUtility.labelWidth; + int indentLevel = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + EditorGUIUtility.labelWidth = Mathf.Min(40f, rect.width * 0.40f); + + s_TempLabel.text = "Speed"; + rect = EditorGUI.PrefixLabel(rect, s_TempLabel); + + EditorGUIUtility.labelWidth = 10f; + rect.width = rect.width * 0.5f - 2f; + + if (names.Length == 1) + { + DoFloat2(rect, names[0]); + } + else + { + DoFloat(rect, names[0], "X"); + rect.x += rect.width + 4f; + DoFloat(rect, names[1], "Y"); + } + + EditorGUIUtility.labelWidth = labelWidth; + EditorGUI.indentLevel = indentLevel; + } + + protected void DoToggle(string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + bool value = EditorGUILayout.Toggle(s_TempLabel, property.floatValue == 1f); + if (EndProperty()) + { + property.floatValue = value ? 1f : 0f; + } + } + + protected void DoFloat(string name, string label) + { + MaterialProperty property = BeginProperty(name); + Rect rect = EditorGUILayout.GetControlRect(); + rect.width = EditorGUIUtility.labelWidth + 55f; + s_TempLabel.text = label; + float value = EditorGUI.FloatField(rect, s_TempLabel, property.floatValue); + if (EndProperty()) + { + property.floatValue = value; + } + } + + protected void DoColor(string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + Color value = EditorGUI.ColorField(EditorGUILayout.GetControlRect(), s_TempLabel, property.colorValue, false, true, true); + if (EndProperty()) + { + property.colorValue = value; + } + } + + void DoFloat(Rect rect, string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + float value = EditorGUI.FloatField(rect, s_TempLabel, property.floatValue); + if (EndProperty()) + { + property.floatValue = value; + } + } + + void DoFloat2(Rect rect, string name) + { + MaterialProperty property = BeginProperty(name); + + float x = EditorGUI.FloatField(rect, "X", property.vectorValue.x); + rect.x += rect.width + 4f; + float y = EditorGUI.FloatField(rect, "Y", property.vectorValue.y); + + if (EndProperty()) + { + property.vectorValue = new Vector2(x, y); + } + } + + protected void DoOffset(string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + Vector2 value = EditorGUI.Vector2Field(EditorGUILayout.GetControlRect(), s_TempLabel, property.vectorValue); + if (EndProperty()) + { + property.vectorValue = value; + } + } + + protected void DoSlider(string name, string label) + { + MaterialProperty property = BeginProperty(name); + Vector2 range = property.rangeLimits; + s_TempLabel.text = label; + float value = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, property.floatValue, range.x, range.y); + if (EndProperty()) + { + property.floatValue = value; + } + } + + protected void DoSlider(string name, Vector2 range, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + float value = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, property.floatValue, range.x, range.y); + if (EndProperty()) + { + property.floatValue = value; + } + } + + protected void DoSlider(string propertyName, string propertyField, string label) + { + MaterialProperty property = BeginProperty(propertyName); + Vector2 range = property.rangeLimits; + s_TempLabel.text = label; + + Vector4 value = property.vectorValue; + + switch (propertyField) + { + case "X": + value.x = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.x, range.x, range.y); + break; + case "Y": + value.y = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.y, range.x, range.y); + break; + case "Z": + value.z = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.z, range.x, range.y); + break; + case "W": + value.w = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.w, range.x, range.y); + break; + } + + if (EndProperty()) + { + property.vectorValue = value; + } + } + + protected void DoSlider(string propertyName, string propertyField, Vector2 range, string label) + { + MaterialProperty property = BeginProperty(propertyName); + s_TempLabel.text = label; + + Vector4 value = property.vectorValue; + + switch (propertyField) + { + case "X": + value.x = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.x, range.x, range.y); + break; + case "Y": + value.y = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.y, range.x, range.y); + break; + case "Z": + value.z = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.z, range.x, range.y); + break; + case "W": + value.w = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.w, range.x, range.y); + break; + } + + if (EndProperty()) + { + property.vectorValue = value; + } + } + + protected void DoVector2(string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + Vector4 value = EditorGUILayout.Vector3Field(s_TempLabel, property.vectorValue); + if (EndProperty()) + { + property.vectorValue = value; + } + } + + protected void DoVector3(string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + Vector4 value = EditorGUILayout.Vector3Field(s_TempLabel, property.vectorValue); + if (EndProperty()) + { + property.vectorValue = value; + } + } + + protected void DoVector(string name, string label, GUIContent[] subLabels) + { + MaterialProperty property = BeginProperty(name); + Rect rect = EditorGUILayout.GetControlRect(); + s_TempLabel.text = label; + rect = EditorGUI.PrefixLabel(rect, s_TempLabel); + Vector4 vector = property.vectorValue; + + float[] values = s_TempFloats[subLabels.Length]; + for (int i = 0; i < subLabels.Length; i++) + { + values[i] = vector[i]; + } + + EditorGUI.MultiFloatField(rect, subLabels, values); + if (EndProperty()) + { + for (int i = 0; i < subLabels.Length; i++) + { + vector[i] = values[i]; + } + + property.vectorValue = vector; + } + } + + void DoDragAndDropBegin() + { + m_DragAndDropMinY = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)).y; + } + + void DoDragAndDropEnd() + { + Rect rect = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)); + Event evt = Event.current; + if (evt.type == EventType.DragUpdated) + { + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + evt.Use(); + } + else if ( + evt.type == EventType.DragPerform && + Rect.MinMaxRect(rect.xMin, m_DragAndDropMinY, rect.xMax, rect.yMax).Contains(evt.mousePosition) + ) + { + DragAndDrop.AcceptDrag(); + evt.Use(); + Material droppedMaterial = DragAndDrop.objectReferences[0] as Material; + if (droppedMaterial && droppedMaterial != m_Material) + { + PerformDrop(droppedMaterial); + } + } + } + + void PerformDrop(Material droppedMaterial) + { + Texture droppedTex = droppedMaterial.GetTexture(ShaderUtilities.ID_MainTex); + if (!droppedTex) + { + return; + } + + Texture currentTex = m_Material.GetTexture(ShaderUtilities.ID_MainTex); + TMP_FontAsset requiredFontAsset = null; + if (droppedTex != currentTex) + { + requiredFontAsset = TMP_EditorUtility.FindMatchingFontAsset(droppedMaterial); + if (!requiredFontAsset) + { + return; + } + } + + foreach (GameObject o in Selection.gameObjects) + { + if (requiredFontAsset) + { + TMP_Text textComponent = o.GetComponent(); + if (textComponent) + { + Undo.RecordObject(textComponent, "Font Asset Change"); + textComponent.font = requiredFontAsset; + } + } + + TMPro_EventManager.ON_DRAG_AND_DROP_MATERIAL_CHANGED(o, m_Material, droppedMaterial); + EditorUtility.SetDirty(o); + } + } + } +} +#endif diff --git a/Scripts/Editor/HDRP/TMP_BaseHDRPUnlitShaderGUI.cs.meta b/Scripts/Editor/HDRP/TMP_BaseHDRPUnlitShaderGUI.cs.meta new file mode 100644 index 0000000..e7f11a6 --- /dev/null +++ b/Scripts/Editor/HDRP/TMP_BaseHDRPUnlitShaderGUI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 645409e9544820042937871953f20509 +timeCreated: 1469844810 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/HDRP/TMP_SDF_HDRPLitShaderGUI.cs b/Scripts/Editor/HDRP/TMP_SDF_HDRPLitShaderGUI.cs new file mode 100644 index 0000000..7d04490 --- /dev/null +++ b/Scripts/Editor/HDRP/TMP_SDF_HDRPLitShaderGUI.cs @@ -0,0 +1,638 @@ +#if HDRP_7_5_OR_NEWER +using UnityEngine; +using UnityEditor; + +using UnityEditor.Rendering.HighDefinition; + +namespace TMPro.EditorUtilities +{ + internal class TMP_SDF_HDRPLitShaderGUI : TMP_BaseHDRPLitShaderGUI + { + // private readonly MaterialUIBlockList uiBlocks = new MaterialUIBlockList + // { + // new SurfaceOptionUIBlock(MaterialUIBlock.Expandable.Base, features: SurfaceOptionUIBlock.Features.Lit), + // new AdvancedOptionsUIBlock(MaterialUIBlock.Expandable.Advance, AdvancedOptionsUIBlock.Features.StandardLit) //AdvancedOptionsUIBlock.Features.Instancing | AdvancedOptionsUIBlock.Features.AddPrecomputedVelocity) + // }; + + static ShaderFeature s_OutlineFeature, s_UnderlayFeature, s_BevelFeature, s_GlowFeature, s_MaskFeature; + + static bool s_Face = true, s_Outline = true, s_Outline2 = true, s_Outline3 = true, s_Underlay = true, s_Lighting = true, s_Glow, s_Bevel, s_Light, s_Bump, s_Env; + + static string[] + s_FaceUVSpeedName = { "_FaceUVSpeed" }, + s_FaceUvSpeedNames = { "_FaceUVSpeedX", "_FaceUVSpeedY" }, + s_OutlineUvSpeedNames = { "_OutlineUVSpeedX", "_OutlineUVSpeedY" }, + s_OutlineUvSpeedName = { "_OutlineUVSpeed" }; + + /// + /// + /// + static TMP_SDF_HDRPLitShaderGUI() + { + s_OutlineFeature = new ShaderFeature() + { + undoLabel = "Outline", + keywords = new[] { "OUTLINE_ON" } + }; + + s_UnderlayFeature = new ShaderFeature() + { + undoLabel = "Underlay", + keywords = new[] { "UNDERLAY_ON", "UNDERLAY_INNER" }, + label = new GUIContent("Underlay Type"), + keywordLabels = new[] + { + new GUIContent("None"), new GUIContent("Normal"), new GUIContent("Inner") + } + }; + + s_BevelFeature = new ShaderFeature() + { + undoLabel = "Bevel", + keywords = new[] { "BEVEL_ON" } + }; + + s_GlowFeature = new ShaderFeature() + { + undoLabel = "Glow", + keywords = new[] { "GLOW_ON" } + }; + + s_MaskFeature = new ShaderFeature() + { + undoLabel = "Mask", + keywords = new[] { "MASK_HARD", "MASK_SOFT" }, + label = new GUIContent("Mask"), + keywordLabels = new[] + { + new GUIContent("Mask Off"), new GUIContent("Mask Hard"), new GUIContent("Mask Soft") + } + }; + } + + /// + /// + /// + public TMP_SDF_HDRPLitShaderGUI() + { + // Remove the ShaderGraphUIBlock to avoid having duplicated properties in the UI. + uiBlocks.RemoveAll(b => b is ShaderGraphUIBlock); + + // Insert the color block just after the Surface Option block. + //uiBlocks.Insert(1, new SurfaceOptionUIBlock(MaterialUIBlock.Expandable.Base, features: SurfaceOptionUIBlock.Features.Lit)); + } + + protected override void DoGUI() + { + s_Face = BeginPanel("Face", s_Face); + if (s_Face) + { + DoFacePanel(); + } + + EndPanel(); + + // Outline panels + DoOutlinePanels(); + + // Underlay panel + s_Underlay = BeginPanel("Underlay", s_Underlay); + if (s_Underlay) + { + DoUnderlayPanel(); + } + + EndPanel(); + + // Lighting panel + DrawLightingPanel(); + + /* + if (m_Material.HasProperty(ShaderUtilities.ID_GlowColor)) + { + s_Glow = BeginPanel("Glow", s_GlowFeature, s_Glow); + if (s_Glow) + { + DoGlowPanel(); + } + + EndPanel(); + } + */ + + s_DebugExtended = BeginPanel("Debug Settings", s_DebugExtended); + if (s_DebugExtended) + { + DoDebugPanelSRP(); + } + EndPanel(); + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + using (var changed = new EditorGUI.ChangeCheckScope()) + { + uiBlocks.OnGUI(m_Editor, m_Properties); + ApplyKeywordsAndPassesIfNeeded(changed.changed, uiBlocks.materials); + } + } + + void DoFacePanel() + { + EditorGUI.indentLevel += 1; + + DoColor("_FaceColor", "Color"); + + if (m_Material.HasProperty(ShaderUtilities.ID_FaceTex)) + { + if (m_Material.HasProperty("_FaceUVSpeedX")) + { + DoTexture2D("_FaceTex", "Texture", true, s_FaceUvSpeedNames); + } + else if (m_Material.HasProperty("_FaceUVSpeed")) + { + DoTexture2D("_FaceTex", "Texture", true, s_FaceUVSpeedName); + } + else + { + DoTexture2D("_FaceTex", "Texture", true); + } + } + + if (m_Material.HasProperty("_Softness")) + { + DoSlider("_Softness", "X", new Vector2(0, 1), "Softness"); + } + + if (m_Material.HasProperty("_OutlineSoftness")) + { + DoSlider("_OutlineSoftness", "Softness"); + } + + if (m_Material.HasProperty(ShaderUtilities.ID_FaceDilate)) + { + DoSlider("_FaceDilate", "Dilate"); + if (m_Material.HasProperty(ShaderUtilities.ID_Shininess)) + { + DoSlider("_FaceShininess", "Gloss"); + } + } + + if (m_Material.HasProperty(ShaderUtilities.ID_IsoPerimeter)) + { + DoSlider("_IsoPerimeter", "X", new Vector2(-1, 1), "Dilate"); + } + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoOutlinePanels() + { + s_Outline = BeginPanel("Outline 1", s_Outline); + if (s_Outline) + DoOutlinePanelWithTexture(1, "Y", "Color"); + + EndPanel(); + + s_Outline2 = BeginPanel("Outline 2", s_Outline2); + if (s_Outline2) + DoOutlinePanel(2, "Z", "Color"); + + EndPanel(); + + s_Outline3 = BeginPanel("Outline 3", s_Outline3); + if (s_Outline3) + DoOutlinePanel(3, "W", "Color"); + + EndPanel(); + } + + void DoOutlinePanel(int outlineID, string propertyField, string label) + { + EditorGUI.indentLevel += 1; + DoColor("_OutlineColor" + outlineID, label); + + if (outlineID != 3) + DoOffset("_OutlineOffset" + outlineID, "Offset"); + else + { + if (m_Material.GetFloat(ShaderUtilities.ID_OutlineMode) == 0) + DoOffset("_OutlineOffset" + outlineID, "Offset"); + } + + DoSlider("_Softness", propertyField, new Vector2(0, 1), "Softness"); + DoSlider("_IsoPerimeter", propertyField, new Vector2(-1, 1), "Dilate"); + + if (outlineID == 3) + { + DoToggle("_OutlineMode", "Outline Mode"); + } + + if (m_Material.HasProperty("_OutlineShininess")) + { + //DoSlider("_OutlineShininess", "Gloss"); + } + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoOutlinePanelWithTexture(int outlineID, string propertyField, string label) + { + EditorGUI.indentLevel += 1; + DoColor("_OutlineColor" + outlineID, label); + if (m_Material.HasProperty(ShaderUtilities.ID_OutlineTex)) + { + if (m_Material.HasProperty("_OutlineUVSpeedX")) + { + DoTexture2D("_OutlineTex", "Texture", true, s_OutlineUvSpeedNames); + } + else if (m_Material.HasProperty("_OutlineUVSpeed")) + { + DoTexture2D("_OutlineTex", "Texture", true, s_OutlineUvSpeedName); + } + else + { + DoTexture2D("_OutlineTex", "Texture", true); + } + } + + DoOffset("_OutlineOffset" + outlineID, "Offset"); + DoSlider("_Softness", propertyField, new Vector2(0, 1), "Softness"); + DoSlider("_IsoPerimeter", propertyField, new Vector2(-1, 1), "Dilate"); + + if (m_Material.HasProperty("_OutlineShininess")) + { + //DoSlider("_OutlineShininess", "Gloss"); + } + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoUnderlayPanel() + { + EditorGUI.indentLevel += 1; + + if (m_Material.HasProperty(ShaderUtilities.ID_IsoPerimeter)) + { + DoColor("_UnderlayColor", "Color"); + DoSlider("_UnderlayOffset", "X", new Vector2(-1, 1), "Offset X"); + DoSlider("_UnderlayOffset", "Y", new Vector2(-1, 1), "Offset Y"); + DoSlider("_UnderlayDilate", new Vector2(-1, 1), "Dilate"); + DoSlider("_UnderlaySoftness", new Vector2(0, 1), "Softness"); + } + else + { + s_UnderlayFeature.DoPopup(m_Editor, m_Material); + DoColor("_UnderlayColor", "Color"); + DoSlider("_UnderlayOffsetX", "Offset X"); + DoSlider("_UnderlayOffsetY", "Offset Y"); + DoSlider("_UnderlayDilate", "Dilate"); + DoSlider("_UnderlaySoftness", "Softness"); + } + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + static GUIContent[] s_BevelTypeLabels = + { + new GUIContent("Outer Bevel"), + new GUIContent("Inner Bevel") + }; + + void DrawLightingPanel() + { + s_Lighting = BeginPanel("Lighting", s_Lighting); + if (s_Lighting) + { + s_Bevel = BeginPanel("Bevel", s_Bevel); + if (s_Bevel) + { + DoBevelPanel(); + } + EndPanel(); + + s_Light = BeginPanel("Local Lighting", s_Light); + if (s_Light) + { + DoLocalLightingPanel(); + } + EndPanel(); + + /* + s_Bump = BeginPanel("Bump Map", s_Bump); + if (s_Bump) + { + DoBumpMapPanel(); + } + + EndPanel(); + + s_Env = BeginPanel("Environment Map", s_Env); + if (s_Env) + { + DoEnvMapPanel(); + } + + EndPanel(); + */ + } + + EndPanel(); + } + + void DoBevelPanel() + { + EditorGUI.indentLevel += 1; + DoPopup("_BevelType", "Type", s_BevelTypeLabels); + DoSlider("_BevelAmount", "Amount"); + DoSlider("_BevelOffset", "Offset"); + DoSlider("_BevelWidth", "Width"); + DoSlider("_BevelRoundness", "Roundness"); + DoSlider("_BevelClamp", "Clamp"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoLocalLightingPanel() + { + EditorGUI.indentLevel += 1; + DoSlider("_LightAngle", "Light Angle"); + DoColor("_SpecularColor", "Specular Color"); + DoSlider("_SpecularPower", "Specular Power"); + DoSlider("_Reflectivity", "Reflectivity Power"); + DoSlider("_Diffuse", "Diffuse Shadow"); + DoSlider("_Ambient", "Ambient Shadow"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoSurfaceLightingPanel() + { + EditorGUI.indentLevel += 1; + DoColor("_SpecColor", "Specular Color"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoBumpMapPanel() + { + EditorGUI.indentLevel += 1; + DoTexture2D("_BumpMap", "Texture"); + DoSlider("_BumpFace", "Face"); + DoSlider("_BumpOutline", "Outline"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoEnvMapPanel() + { + EditorGUI.indentLevel += 1; + DoColor("_ReflectFaceColor", "Face Color"); + DoColor("_ReflectOutlineColor", "Outline Color"); + DoCubeMap("_Cube", "Texture"); + DoVector3("_EnvMatrixRotation", "Rotation"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoGlowPanel() + { + EditorGUI.indentLevel += 1; + DoColor("_GlowColor", "Color"); + DoSlider("_GlowOffset", "Offset"); + DoSlider("_GlowInner", "Inner"); + DoSlider("_GlowOuter", "Outer"); + DoSlider("_GlowPower", "Power"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoDebugPanel() + { + EditorGUI.indentLevel += 1; + DoTexture2D("_MainTex", "Font Atlas"); + DoFloat("_GradientScale", "Gradient Scale"); + DoFloat("_TextureWidth", "Texture Width"); + DoFloat("_TextureHeight", "Texture Height"); + EditorGUILayout.Space(); + DoFloat("_ScaleX", "Scale X"); + DoFloat("_ScaleY", "Scale Y"); + + if (m_Material.HasProperty(ShaderUtilities.ID_Sharpness)) + DoSlider("_Sharpness", "Sharpness"); + + DoSlider("_PerspectiveFilter", "Perspective Filter"); + EditorGUILayout.Space(); + DoFloat("_VertexOffsetX", "Offset X"); + DoFloat("_VertexOffsetY", "Offset Y"); + + if (m_Material.HasProperty(ShaderUtilities.ID_MaskCoord)) + { + EditorGUILayout.Space(); + s_MaskFeature.ReadState(m_Material); + s_MaskFeature.DoPopup(m_Editor, m_Material); + if (s_MaskFeature.Active) + { + DoMaskSubgroup(); + } + + EditorGUILayout.Space(); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + else if (m_Material.HasProperty("_MaskTex")) + { + DoMaskTexSubgroup(); + } + else if (m_Material.HasProperty(ShaderUtilities.ID_MaskSoftnessX)) + { + EditorGUILayout.Space(); + DoFloat("_MaskSoftnessX", "Softness X"); + DoFloat("_MaskSoftnessY", "Softness Y"); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + + if (m_Material.HasProperty(ShaderUtilities.ID_StencilID)) + { + EditorGUILayout.Space(); + DoFloat("_Stencil", "Stencil ID"); + DoFloat("_StencilComp", "Stencil Comp"); + } + + EditorGUILayout.Space(); + + EditorGUI.BeginChangeCheck(); + bool useRatios = EditorGUILayout.Toggle("Use Ratios", !m_Material.IsKeywordEnabled("RATIOS_OFF")); + if (EditorGUI.EndChangeCheck()) + { + m_Editor.RegisterPropertyChangeUndo("Use Ratios"); + if (useRatios) + { + m_Material.DisableKeyword("RATIOS_OFF"); + } + else + { + m_Material.EnableKeyword("RATIOS_OFF"); + } + } + + if (m_Material.HasProperty(ShaderUtilities.ShaderTag_CullMode)) + { + EditorGUILayout.Space(); + DoPopup("_CullMode", "Cull Mode", s_CullingTypeLabels); + } + + EditorGUILayout.Space(); + + EditorGUI.BeginDisabledGroup(true); + DoFloat("_ScaleRatioA", "Scale Ratio A"); + DoFloat("_ScaleRatioB", "Scale Ratio B"); + DoFloat("_ScaleRatioC", "Scale Ratio C"); + EditorGUI.EndDisabledGroup(); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoDebugPanelSRP() + { + EditorGUI.indentLevel += 1; + DoTexture2D("_MainTex", "Font Atlas"); + DoFloat("_GradientScale", "Gradient Scale"); + //DoFloat("_TextureWidth", "Texture Width"); + //DoFloat("_TextureHeight", "Texture Height"); + EditorGUILayout.Space(); + + /* + DoFloat("_ScaleX", "Scale X"); + DoFloat("_ScaleY", "Scale Y"); + + if (m_Material.HasProperty(ShaderUtilities.ID_Sharpness)) + DoSlider("_Sharpness", "Sharpness"); + + DoSlider("_PerspectiveFilter", "Perspective Filter"); + EditorGUILayout.Space(); + DoFloat("_VertexOffsetX", "Offset X"); + DoFloat("_VertexOffsetY", "Offset Y"); + + if (m_Material.HasProperty(ShaderUtilities.ID_MaskCoord)) + { + EditorGUILayout.Space(); + s_MaskFeature.ReadState(m_Material); + s_MaskFeature.DoPopup(m_Editor, m_Material); + if (s_MaskFeature.Active) + { + DoMaskSubgroup(); + } + + EditorGUILayout.Space(); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + else if (m_Material.HasProperty("_MaskTex")) + { + DoMaskTexSubgroup(); + } + else if (m_Material.HasProperty(ShaderUtilities.ID_MaskSoftnessX)) + { + EditorGUILayout.Space(); + DoFloat("_MaskSoftnessX", "Softness X"); + DoFloat("_MaskSoftnessY", "Softness Y"); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + + if (m_Material.HasProperty(ShaderUtilities.ID_StencilID)) + { + EditorGUILayout.Space(); + DoFloat("_Stencil", "Stencil ID"); + DoFloat("_StencilComp", "Stencil Comp"); + } + + EditorGUILayout.Space(); + + EditorGUI.BeginChangeCheck(); + bool useRatios = EditorGUILayout.Toggle("Use Ratios", !m_Material.IsKeywordEnabled("RATIOS_OFF")); + if (EditorGUI.EndChangeCheck()) + { + m_Editor.RegisterPropertyChangeUndo("Use Ratios"); + if (useRatios) + { + m_Material.DisableKeyword("RATIOS_OFF"); + } + else + { + m_Material.EnableKeyword("RATIOS_OFF"); + } + } + */ + if (m_Material.HasProperty(ShaderUtilities.ShaderTag_CullMode)) + { + EditorGUILayout.Space(); + DoPopup("_CullMode", "Cull Mode", s_CullingTypeLabels); + } + + EditorGUILayout.Space(); + /* + EditorGUI.BeginDisabledGroup(true); + DoFloat("_ScaleRatioA", "Scale Ratio A"); + DoFloat("_ScaleRatioB", "Scale Ratio B"); + DoFloat("_ScaleRatioC", "Scale Ratio C"); + EditorGUI.EndDisabledGroup(); + */ + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoMaskSubgroup() + { + DoVector("_MaskCoord", "Mask Bounds", s_XywhVectorLabels); + if (Selection.activeGameObject != null) + { + Renderer renderer = Selection.activeGameObject.GetComponent(); + if (renderer != null) + { + Rect rect = EditorGUILayout.GetControlRect(); + rect.x += EditorGUIUtility.labelWidth; + rect.width -= EditorGUIUtility.labelWidth; + if (GUI.Button(rect, "Match Renderer Bounds")) + { + FindProperty("_MaskCoord", m_Properties).vectorValue = new Vector4( + 0, + 0, + Mathf.Round(renderer.bounds.extents.x * 1000) / 1000, + Mathf.Round(renderer.bounds.extents.y * 1000) / 1000 + ); + } + } + } + + if (s_MaskFeature.State == 1) + { + DoFloat("_MaskSoftnessX", "Softness X"); + DoFloat("_MaskSoftnessY", "Softness Y"); + } + } + + void DoMaskTexSubgroup() + { + EditorGUILayout.Space(); + DoTexture2D("_MaskTex", "Mask Texture"); + DoToggle("_MaskInverse", "Inverse Mask"); + DoColor("_MaskEdgeColor", "Edge Color"); + DoSlider("_MaskEdgeSoftness", "Edge Softness"); + DoSlider("_MaskWipeControl", "Wipe Position"); + DoFloat("_MaskSoftnessX", "Softness X"); + DoFloat("_MaskSoftnessY", "Softness Y"); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + + // protected override void SetupMaterialKeywordsAndPassInternal(Material material) + // { + // BaseLitGUI.SetupBaseLitKeywords(material); + // BaseLitGUI.SetupBaseLitMaterialPass(material); + // } + } +} +#endif diff --git a/Scripts/Editor/HDRP/TMP_SDF_HDRPLitShaderGUI.cs.meta b/Scripts/Editor/HDRP/TMP_SDF_HDRPLitShaderGUI.cs.meta new file mode 100644 index 0000000..9fbf55e --- /dev/null +++ b/Scripts/Editor/HDRP/TMP_SDF_HDRPLitShaderGUI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 85016528879d5d644981050d1d0a4368 +timeCreated: 1469844718 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/HDRP/TMP_SDF_HDRPUnlitShaderGUI.cs b/Scripts/Editor/HDRP/TMP_SDF_HDRPUnlitShaderGUI.cs new file mode 100644 index 0000000..a84b3c8 --- /dev/null +++ b/Scripts/Editor/HDRP/TMP_SDF_HDRPUnlitShaderGUI.cs @@ -0,0 +1,638 @@ +#if HDRP_7_5_OR_NEWER +using UnityEngine; +using UnityEditor; + +using UnityEditor.Rendering.HighDefinition; + +namespace TMPro.EditorUtilities +{ + internal class TMP_SDF_HDRPUnlitShaderGUI : TMP_BaseHDRPUnlitShaderGUI + { + // private readonly MaterialUIBlockList uiBlocks = new MaterialUIBlockList + // { + // new SurfaceOptionUIBlock(MaterialUIBlock.Expandable.Base, features: SurfaceOptionUIBlock.Features.Lit), + // new AdvancedOptionsUIBlock(MaterialUIBlock.Expandable.Advance, AdvancedOptionsUIBlock.Features.StandardLit) //AdvancedOptionsUIBlock.Features.Instancing | AdvancedOptionsUIBlock.Features.AddPrecomputedVelocity) + // }; + + static ShaderFeature s_OutlineFeature, s_UnderlayFeature, s_BevelFeature, s_GlowFeature, s_MaskFeature; + + static bool s_Face = true, s_Outline = true, s_Outline2 = true, s_Outline3 = true, s_Underlay = true, s_Lighting = true, s_Glow, s_Bevel, s_Light, s_Bump, s_Env; + + static string[] + s_FaceUVSpeedName = { "_FaceUVSpeed" }, + s_FaceUvSpeedNames = { "_FaceUVSpeedX", "_FaceUVSpeedY" }, + s_OutlineUvSpeedNames = { "_OutlineUVSpeedX", "_OutlineUVSpeedY" }, + s_OutlineUvSpeedName = { "_OutlineUVSpeed" }; + + /// + /// + /// + static TMP_SDF_HDRPUnlitShaderGUI() + { + s_OutlineFeature = new ShaderFeature() + { + undoLabel = "Outline", + keywords = new[] { "OUTLINE_ON" } + }; + + s_UnderlayFeature = new ShaderFeature() + { + undoLabel = "Underlay", + keywords = new[] { "UNDERLAY_ON", "UNDERLAY_INNER" }, + label = new GUIContent("Underlay Type"), + keywordLabels = new[] + { + new GUIContent("None"), new GUIContent("Normal"), new GUIContent("Inner") + } + }; + + s_BevelFeature = new ShaderFeature() + { + undoLabel = "Bevel", + keywords = new[] { "BEVEL_ON" } + }; + + s_GlowFeature = new ShaderFeature() + { + undoLabel = "Glow", + keywords = new[] { "GLOW_ON" } + }; + + s_MaskFeature = new ShaderFeature() + { + undoLabel = "Mask", + keywords = new[] { "MASK_HARD", "MASK_SOFT" }, + label = new GUIContent("Mask"), + keywordLabels = new[] + { + new GUIContent("Mask Off"), new GUIContent("Mask Hard"), new GUIContent("Mask Soft") + } + }; + } + + /// + /// + /// + public TMP_SDF_HDRPUnlitShaderGUI() + { + // Remove the ShaderGraphUIBlock to avoid having duplicated properties in the UI. + uiBlocks.RemoveAll(b => b is ShaderGraphUIBlock); + + // Insert the color block just after the Surface Option block. + //uiBlocks.Insert(1, new SurfaceOptionUIBlock(MaterialUIBlock.Expandable.Base, features: SurfaceOptionUIBlock.Features.Lit)); + } + + protected override void DoGUI() + { + s_Face = BeginPanel("Face", s_Face); + if (s_Face) + { + DoFacePanel(); + } + + EndPanel(); + + // Outline panels + DoOutlinePanels(); + + // Underlay panel + s_Underlay = BeginPanel("Underlay", s_Underlay); + if (s_Underlay) + { + DoUnderlayPanel(); + } + + EndPanel(); + + // Lighting panel + DrawLightingPanel(); + + /* + if (m_Material.HasProperty(ShaderUtilities.ID_GlowColor)) + { + s_Glow = BeginPanel("Glow", s_GlowFeature, s_Glow); + if (s_Glow) + { + DoGlowPanel(); + } + + EndPanel(); + } + */ + + s_DebugExtended = BeginPanel("Debug Settings", s_DebugExtended); + if (s_DebugExtended) + { + DoDebugPanelSRP(); + } + EndPanel(); + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + using (var changed = new EditorGUI.ChangeCheckScope()) + { + uiBlocks.OnGUI(m_Editor, m_Properties); + ApplyKeywordsAndPassesIfNeeded(changed.changed, uiBlocks.materials); + } + } + + void DoFacePanel() + { + EditorGUI.indentLevel += 1; + + DoColor("_FaceColor", "Color"); + + if (m_Material.HasProperty(ShaderUtilities.ID_FaceTex)) + { + if (m_Material.HasProperty("_FaceUVSpeedX")) + { + DoTexture2D("_FaceTex", "Texture", true, s_FaceUvSpeedNames); + } + else if (m_Material.HasProperty("_FaceUVSpeed")) + { + DoTexture2D("_FaceTex", "Texture", true, s_FaceUVSpeedName); + } + else + { + DoTexture2D("_FaceTex", "Texture", true); + } + } + + if (m_Material.HasProperty("_Softness")) + { + DoSlider("_Softness", "X", new Vector2(0, 1), "Softness"); + } + + if (m_Material.HasProperty("_OutlineSoftness")) + { + DoSlider("_OutlineSoftness", "Softness"); + } + + if (m_Material.HasProperty(ShaderUtilities.ID_FaceDilate)) + { + DoSlider("_FaceDilate", "Dilate"); + if (m_Material.HasProperty(ShaderUtilities.ID_Shininess)) + { + DoSlider("_FaceShininess", "Gloss"); + } + } + + if (m_Material.HasProperty(ShaderUtilities.ID_IsoPerimeter)) + { + DoSlider("_IsoPerimeter", "X", new Vector2(-1, 1), "Dilate"); + } + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoOutlinePanels() + { + s_Outline = BeginPanel("Outline 1", s_Outline); + if (s_Outline) + DoOutlinePanelWithTexture(1, "Y", "Color"); + + EndPanel(); + + s_Outline2 = BeginPanel("Outline 2", s_Outline2); + if (s_Outline2) + DoOutlinePanel(2, "Z", "Color"); + + EndPanel(); + + s_Outline3 = BeginPanel("Outline 3", s_Outline3); + if (s_Outline3) + DoOutlinePanel(3, "W", "Color"); + + EndPanel(); + } + + void DoOutlinePanel(int outlineID, string propertyField, string label) + { + EditorGUI.indentLevel += 1; + DoColor("_OutlineColor" + outlineID, label); + + if (outlineID != 3) + DoOffset("_OutlineOffset" + outlineID, "Offset"); + else + { + if (m_Material.GetFloat(ShaderUtilities.ID_OutlineMode) == 0) + DoOffset("_OutlineOffset" + outlineID, "Offset"); + } + + DoSlider("_Softness", propertyField, new Vector2(0, 1), "Softness"); + DoSlider("_IsoPerimeter", propertyField, new Vector2(-1, 1), "Dilate"); + + if (outlineID == 3) + { + DoToggle("_OutlineMode", "Outline Mode"); + } + + if (m_Material.HasProperty("_OutlineShininess")) + { + //DoSlider("_OutlineShininess", "Gloss"); + } + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoOutlinePanelWithTexture(int outlineID, string propertyField, string label) + { + EditorGUI.indentLevel += 1; + DoColor("_OutlineColor" + outlineID, label); + if (m_Material.HasProperty(ShaderUtilities.ID_OutlineTex)) + { + if (m_Material.HasProperty("_OutlineUVSpeedX")) + { + DoTexture2D("_OutlineTex", "Texture", true, s_OutlineUvSpeedNames); + } + else if (m_Material.HasProperty("_OutlineUVSpeed")) + { + DoTexture2D("_OutlineTex", "Texture", true, s_OutlineUvSpeedName); + } + else + { + DoTexture2D("_OutlineTex", "Texture", true); + } + } + + DoOffset("_OutlineOffset" + outlineID, "Offset"); + DoSlider("_Softness", propertyField, new Vector2(0, 1), "Softness"); + DoSlider("_IsoPerimeter", propertyField, new Vector2(-1, 1), "Dilate"); + + if (m_Material.HasProperty("_OutlineShininess")) + { + //DoSlider("_OutlineShininess", "Gloss"); + } + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoUnderlayPanel() + { + EditorGUI.indentLevel += 1; + + if (m_Material.HasProperty(ShaderUtilities.ID_IsoPerimeter)) + { + DoColor("_UnderlayColor", "Color"); + DoSlider("_UnderlayOffset", "X", new Vector2(-1, 1), "Offset X"); + DoSlider("_UnderlayOffset", "Y", new Vector2(-1, 1), "Offset Y"); + DoSlider("_UnderlayDilate", new Vector2(-1, 1), "Dilate"); + DoSlider("_UnderlaySoftness", new Vector2(0, 1), "Softness"); + } + else + { + s_UnderlayFeature.DoPopup(m_Editor, m_Material); + DoColor("_UnderlayColor", "Color"); + DoSlider("_UnderlayOffsetX", "Offset X"); + DoSlider("_UnderlayOffsetY", "Offset Y"); + DoSlider("_UnderlayDilate", "Dilate"); + DoSlider("_UnderlaySoftness", "Softness"); + } + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + static GUIContent[] s_BevelTypeLabels = + { + new GUIContent("Outer Bevel"), + new GUIContent("Inner Bevel") + }; + + void DrawLightingPanel() + { + s_Lighting = BeginPanel("Lighting", s_Lighting); + if (s_Lighting) + { + s_Bevel = BeginPanel("Bevel", s_Bevel); + if (s_Bevel) + { + DoBevelPanel(); + } + EndPanel(); + + s_Light = BeginPanel("Local Lighting", s_Light); + if (s_Light) + { + DoLocalLightingPanel(); + } + EndPanel(); + + /* + s_Bump = BeginPanel("Bump Map", s_Bump); + if (s_Bump) + { + DoBumpMapPanel(); + } + + EndPanel(); + + s_Env = BeginPanel("Environment Map", s_Env); + if (s_Env) + { + DoEnvMapPanel(); + } + + EndPanel(); + */ + } + + EndPanel(); + } + + void DoBevelPanel() + { + EditorGUI.indentLevel += 1; + DoPopup("_BevelType", "Type", s_BevelTypeLabels); + DoSlider("_BevelAmount", "Amount"); + DoSlider("_BevelOffset", "Offset"); + DoSlider("_BevelWidth", "Width"); + DoSlider("_BevelRoundness", "Roundness"); + DoSlider("_BevelClamp", "Clamp"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoLocalLightingPanel() + { + EditorGUI.indentLevel += 1; + DoSlider("_LightAngle", "Light Angle"); + DoColor("_SpecularColor", "Specular Color"); + DoSlider("_SpecularPower", "Specular Power"); + DoSlider("_Reflectivity", "Reflectivity Power"); + DoSlider("_Diffuse", "Diffuse Shadow"); + DoSlider("_Ambient", "Ambient Shadow"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoSurfaceLightingPanel() + { + EditorGUI.indentLevel += 1; + DoColor("_SpecColor", "Specular Color"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoBumpMapPanel() + { + EditorGUI.indentLevel += 1; + DoTexture2D("_BumpMap", "Texture"); + DoSlider("_BumpFace", "Face"); + DoSlider("_BumpOutline", "Outline"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoEnvMapPanel() + { + EditorGUI.indentLevel += 1; + DoColor("_ReflectFaceColor", "Face Color"); + DoColor("_ReflectOutlineColor", "Outline Color"); + DoCubeMap("_Cube", "Texture"); + DoVector3("_EnvMatrixRotation", "Rotation"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoGlowPanel() + { + EditorGUI.indentLevel += 1; + DoColor("_GlowColor", "Color"); + DoSlider("_GlowOffset", "Offset"); + DoSlider("_GlowInner", "Inner"); + DoSlider("_GlowOuter", "Outer"); + DoSlider("_GlowPower", "Power"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoDebugPanel() + { + EditorGUI.indentLevel += 1; + DoTexture2D("_MainTex", "Font Atlas"); + DoFloat("_GradientScale", "Gradient Scale"); + DoFloat("_TextureWidth", "Texture Width"); + DoFloat("_TextureHeight", "Texture Height"); + EditorGUILayout.Space(); + DoFloat("_ScaleX", "Scale X"); + DoFloat("_ScaleY", "Scale Y"); + + if (m_Material.HasProperty(ShaderUtilities.ID_Sharpness)) + DoSlider("_Sharpness", "Sharpness"); + + DoSlider("_PerspectiveFilter", "Perspective Filter"); + EditorGUILayout.Space(); + DoFloat("_VertexOffsetX", "Offset X"); + DoFloat("_VertexOffsetY", "Offset Y"); + + if (m_Material.HasProperty(ShaderUtilities.ID_MaskCoord)) + { + EditorGUILayout.Space(); + s_MaskFeature.ReadState(m_Material); + s_MaskFeature.DoPopup(m_Editor, m_Material); + if (s_MaskFeature.Active) + { + DoMaskSubgroup(); + } + + EditorGUILayout.Space(); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + else if (m_Material.HasProperty("_MaskTex")) + { + DoMaskTexSubgroup(); + } + else if (m_Material.HasProperty(ShaderUtilities.ID_MaskSoftnessX)) + { + EditorGUILayout.Space(); + DoFloat("_MaskSoftnessX", "Softness X"); + DoFloat("_MaskSoftnessY", "Softness Y"); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + + if (m_Material.HasProperty(ShaderUtilities.ID_StencilID)) + { + EditorGUILayout.Space(); + DoFloat("_Stencil", "Stencil ID"); + DoFloat("_StencilComp", "Stencil Comp"); + } + + EditorGUILayout.Space(); + + EditorGUI.BeginChangeCheck(); + bool useRatios = EditorGUILayout.Toggle("Use Ratios", !m_Material.IsKeywordEnabled("RATIOS_OFF")); + if (EditorGUI.EndChangeCheck()) + { + m_Editor.RegisterPropertyChangeUndo("Use Ratios"); + if (useRatios) + { + m_Material.DisableKeyword("RATIOS_OFF"); + } + else + { + m_Material.EnableKeyword("RATIOS_OFF"); + } + } + + if (m_Material.HasProperty(ShaderUtilities.ShaderTag_CullMode)) + { + EditorGUILayout.Space(); + DoPopup("_CullMode", "Cull Mode", s_CullingTypeLabels); + } + + EditorGUILayout.Space(); + + EditorGUI.BeginDisabledGroup(true); + DoFloat("_ScaleRatioA", "Scale Ratio A"); + DoFloat("_ScaleRatioB", "Scale Ratio B"); + DoFloat("_ScaleRatioC", "Scale Ratio C"); + EditorGUI.EndDisabledGroup(); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoDebugPanelSRP() + { + EditorGUI.indentLevel += 1; + DoTexture2D("_MainTex", "Font Atlas"); + DoFloat("_GradientScale", "Gradient Scale"); + //DoFloat("_TextureWidth", "Texture Width"); + //DoFloat("_TextureHeight", "Texture Height"); + EditorGUILayout.Space(); + + /* + DoFloat("_ScaleX", "Scale X"); + DoFloat("_ScaleY", "Scale Y"); + + if (m_Material.HasProperty(ShaderUtilities.ID_Sharpness)) + DoSlider("_Sharpness", "Sharpness"); + + DoSlider("_PerspectiveFilter", "Perspective Filter"); + EditorGUILayout.Space(); + DoFloat("_VertexOffsetX", "Offset X"); + DoFloat("_VertexOffsetY", "Offset Y"); + + if (m_Material.HasProperty(ShaderUtilities.ID_MaskCoord)) + { + EditorGUILayout.Space(); + s_MaskFeature.ReadState(m_Material); + s_MaskFeature.DoPopup(m_Editor, m_Material); + if (s_MaskFeature.Active) + { + DoMaskSubgroup(); + } + + EditorGUILayout.Space(); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + else if (m_Material.HasProperty("_MaskTex")) + { + DoMaskTexSubgroup(); + } + else if (m_Material.HasProperty(ShaderUtilities.ID_MaskSoftnessX)) + { + EditorGUILayout.Space(); + DoFloat("_MaskSoftnessX", "Softness X"); + DoFloat("_MaskSoftnessY", "Softness Y"); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + + if (m_Material.HasProperty(ShaderUtilities.ID_StencilID)) + { + EditorGUILayout.Space(); + DoFloat("_Stencil", "Stencil ID"); + DoFloat("_StencilComp", "Stencil Comp"); + } + + EditorGUILayout.Space(); + + EditorGUI.BeginChangeCheck(); + bool useRatios = EditorGUILayout.Toggle("Use Ratios", !m_Material.IsKeywordEnabled("RATIOS_OFF")); + if (EditorGUI.EndChangeCheck()) + { + m_Editor.RegisterPropertyChangeUndo("Use Ratios"); + if (useRatios) + { + m_Material.DisableKeyword("RATIOS_OFF"); + } + else + { + m_Material.EnableKeyword("RATIOS_OFF"); + } + } + */ + if (m_Material.HasProperty(ShaderUtilities.ShaderTag_CullMode)) + { + EditorGUILayout.Space(); + DoPopup("_CullMode", "Cull Mode", s_CullingTypeLabels); + } + + EditorGUILayout.Space(); + /* + EditorGUI.BeginDisabledGroup(true); + DoFloat("_ScaleRatioA", "Scale Ratio A"); + DoFloat("_ScaleRatioB", "Scale Ratio B"); + DoFloat("_ScaleRatioC", "Scale Ratio C"); + EditorGUI.EndDisabledGroup(); + */ + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoMaskSubgroup() + { + DoVector("_MaskCoord", "Mask Bounds", s_XywhVectorLabels); + if (Selection.activeGameObject != null) + { + Renderer renderer = Selection.activeGameObject.GetComponent(); + if (renderer != null) + { + Rect rect = EditorGUILayout.GetControlRect(); + rect.x += EditorGUIUtility.labelWidth; + rect.width -= EditorGUIUtility.labelWidth; + if (GUI.Button(rect, "Match Renderer Bounds")) + { + FindProperty("_MaskCoord", m_Properties).vectorValue = new Vector4( + 0, + 0, + Mathf.Round(renderer.bounds.extents.x * 1000) / 1000, + Mathf.Round(renderer.bounds.extents.y * 1000) / 1000 + ); + } + } + } + + if (s_MaskFeature.State == 1) + { + DoFloat("_MaskSoftnessX", "Softness X"); + DoFloat("_MaskSoftnessY", "Softness Y"); + } + } + + void DoMaskTexSubgroup() + { + EditorGUILayout.Space(); + DoTexture2D("_MaskTex", "Mask Texture"); + DoToggle("_MaskInverse", "Inverse Mask"); + DoColor("_MaskEdgeColor", "Edge Color"); + DoSlider("_MaskEdgeSoftness", "Edge Softness"); + DoSlider("_MaskWipeControl", "Wipe Position"); + DoFloat("_MaskSoftnessX", "Softness X"); + DoFloat("_MaskSoftnessY", "Softness Y"); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + + // protected override void SetupMaterialKeywordsAndPassInternal(Material material) + // { + // BaseLitGUI.SetupBaseLitKeywords(material); + // BaseLitGUI.SetupBaseLitMaterialPass(material); + // } + } +} +#endif diff --git a/Scripts/Editor/HDRP/TMP_SDF_HDRPUnlitShaderGUI.cs.meta b/Scripts/Editor/HDRP/TMP_SDF_HDRPUnlitShaderGUI.cs.meta new file mode 100644 index 0000000..f140640 --- /dev/null +++ b/Scripts/Editor/HDRP/TMP_SDF_HDRPUnlitShaderGUI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: bad96c2cfa78a124cb8ec890d2386dfe +timeCreated: 1469844718 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/TMP_BaseEditorPanel.cs b/Scripts/Editor/TMP_BaseEditorPanel.cs index 709cc9d..44d2e48 100644 --- a/Scripts/Editor/TMP_BaseEditorPanel.cs +++ b/Scripts/Editor/TMP_BaseEditorPanel.cs @@ -44,7 +44,7 @@ public abstract class TMP_BaseEditorPanel : Editor static readonly GUIContent k_LineSpacingLabel = new GUIContent("Line"); static readonly GUIContent k_ParagraphSpacingLabel = new GUIContent("Paragraph"); - static readonly GUIContent k_AlignmentLabel = new GUIContent("Alignment", "Horizontal and vertical aligment of the text within its container."); + static readonly GUIContent k_AlignmentLabel = new GUIContent("Alignment", "Horizontal and vertical alignment of the text within its container."); static readonly GUIContent k_WrapMixLabel = new GUIContent("Wrap Mix (W <-> C)", "How much to favor words versus characters when distributing the text."); static readonly GUIContent k_WrappingLabel = new GUIContent("Wrapping", "Wraps text to the next line when reaching the edge of the container."); @@ -140,7 +140,7 @@ protected struct Foldout protected SerializedProperty m_VerticalMappingProp; protected SerializedProperty m_UvLineOffsetProp; - protected SerializedProperty m_EnableWordWrappingProp; + protected SerializedProperty m_TextWrappingModeProp; protected SerializedProperty m_WordWrappingRatiosProp; protected SerializedProperty m_TextOverflowModeProp; protected SerializedProperty m_PageToDisplayProp; @@ -219,7 +219,7 @@ protected virtual void OnEnable() m_VerticalMappingProp = serializedObject.FindProperty("m_verticalMapping"); m_UvLineOffsetProp = serializedObject.FindProperty("m_uvLineOffset"); - m_EnableWordWrappingProp = serializedObject.FindProperty("m_enableWordWrapping"); + m_TextWrappingModeProp = serializedObject.FindProperty("m_TextWrappingMode"); m_WordWrappingRatiosProp = serializedObject.FindProperty("m_wordWrappingRatios"); m_TextOverflowModeProp = serializedObject.FindProperty("m_overflowMode"); m_PageToDisplayProp = serializedObject.FindProperty("m_pageToDisplay"); @@ -993,13 +993,12 @@ void DrawWrappingOverflow() { // TEXT WRAPPING Rect rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); - EditorGUI.BeginProperty(rect, k_WrappingLabel, m_EnableWordWrappingProp); + EditorGUI.BeginProperty(rect, k_WrappingLabel, m_TextWrappingModeProp); EditorGUI.BeginChangeCheck(); - int wrapSelection = EditorGUI.Popup(rect, k_WrappingLabel, m_EnableWordWrappingProp.boolValue ? 1 : 0, k_WrappingOptions); + EditorGUI.PropertyField(rect, m_TextWrappingModeProp); if (EditorGUI.EndChangeCheck()) { - m_EnableWordWrappingProp.boolValue = wrapSelection == 1; m_HavePropertiesChanged = true; } diff --git a/Scripts/Editor/TMP_BaseShaderGUI.cs b/Scripts/Editor/TMP_BaseShaderGUI.cs index b03140b..64de270 100644 --- a/Scripts/Editor/TMP_BaseShaderGUI.cs +++ b/Scripts/Editor/TMP_BaseShaderGUI.cs @@ -127,6 +127,7 @@ static TMP_BaseShaderGUI() protected MaterialEditor m_Editor; protected Material m_Material; + private int m_ShaderID; protected MaterialProperty[] m_Properties; @@ -141,7 +142,7 @@ void PrepareGUI() { // There's been at least one undo/redo since the last time this GUI got constructed. // Maybe the undo/redo was for this material? Assume that is was. - TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material as Material); + TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material); } s_LastSeenUndoRedoCount = s_UndoRedoCount; @@ -154,9 +155,7 @@ public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] pro this.m_Properties = properties; if (m_IsNewGUI) - { PrepareGUI(); - } DoDragAndDropBegin(); EditorGUI.BeginChangeCheck(); @@ -169,6 +168,13 @@ public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] pro DoDragAndDropEnd(); } + public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader) + { + base.AssignNewShaderToMaterial(material, oldShader, newShader); + + TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, material); + } + /// Override this method to create the specific shader GUI. protected abstract void DoGUI(); @@ -331,7 +337,7 @@ void DoTilingOffset(Rect rect, MaterialProperty property) float labelWidth = EditorGUIUtility.labelWidth; int indentLevel = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; - EditorGUIUtility.labelWidth = Mathf.Min(37f, rect.width * 0.40f); + EditorGUIUtility.labelWidth = Mathf.Min(40f, rect.width * 0.40f); Vector4 vector = property.textureScaleAndOffset; @@ -381,7 +387,7 @@ protected void DoUVSpeed(Rect rect, string[] names) float labelWidth = EditorGUIUtility.labelWidth; int indentLevel = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; - EditorGUIUtility.labelWidth = Mathf.Min(37f, rect.width * 0.40f); + EditorGUIUtility.labelWidth = Mathf.Min(40f, rect.width * 0.40f); s_TempLabel.text = "Speed"; rect = EditorGUI.PrefixLabel(rect, s_TempLabel); @@ -464,6 +470,17 @@ void DoFloat2(Rect rect, string name) } } + protected void DoOffset(string name, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + Vector2 value = EditorGUI.Vector2Field(EditorGUILayout.GetControlRect(), s_TempLabel, property.vectorValue); + if (EndProperty()) + { + property.vectorValue = value; + } + } + protected void DoSlider(string name, string label) { MaterialProperty property = BeginProperty(name); @@ -476,6 +493,17 @@ protected void DoSlider(string name, string label) } } + protected void DoSlider(string name, Vector2 range, string label) + { + MaterialProperty property = BeginProperty(name); + s_TempLabel.text = label; + float value = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, property.floatValue, range.x, range.y); + if (EndProperty()) + { + property.floatValue = value; + } + } + protected void DoSlider(string propertyName, string propertyField, string label) { MaterialProperty property = BeginProperty(propertyName); @@ -506,6 +534,35 @@ protected void DoSlider(string propertyName, string propertyField, string label) } } + protected void DoSlider(string propertyName, string propertyField, Vector2 range, string label) + { + MaterialProperty property = BeginProperty(propertyName); + s_TempLabel.text = label; + + Vector4 value = property.vectorValue; + + switch (propertyField) + { + case "X": + value.x = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.x, range.x, range.y); + break; + case "Y": + value.y = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.y, range.x, range.y); + break; + case "Z": + value.z = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.z, range.x, range.y); + break; + case "W": + value.w = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.w, range.x, range.y); + break; + } + + if (EndProperty()) + { + property.vectorValue = value; + } + } + protected void DoVector2(string name, string label) { MaterialProperty property = BeginProperty(name); @@ -554,6 +611,21 @@ protected void DoVector(string name, string label, GUIContent[] subLabels) } } + bool IsNewShader() + { + if (m_Material == null) + return false; + + int currentShaderID = m_Material.shader.GetInstanceID(); + + if (m_ShaderID == currentShaderID) + return false; + + m_ShaderID = currentShaderID; + + return true; + } + void DoDragAndDropBegin() { m_DragAndDropMinY = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)).y; @@ -563,15 +635,13 @@ void DoDragAndDropEnd() { Rect rect = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)); Event evt = Event.current; + if (evt.type == EventType.DragUpdated) { DragAndDrop.visualMode = DragAndDropVisualMode.Generic; evt.Use(); } - else if ( - evt.type == EventType.DragPerform && - Rect.MinMaxRect(rect.xMin, m_DragAndDropMinY, rect.xMax, rect.yMax).Contains(evt.mousePosition) - ) + else if (evt.type == EventType.DragPerform && Rect.MinMaxRect(rect.xMin, m_DragAndDropMinY, rect.xMax, rect.yMax).Contains(evt.mousePosition)) { DragAndDrop.AcceptDrag(); evt.Use(); @@ -581,6 +651,11 @@ void DoDragAndDropEnd() PerformDrop(droppedMaterial); } } + else if (evt.type == EventType.DragExited) + { + if (IsNewShader()) + TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material); + } } void PerformDrop(Material droppedMaterial) diff --git a/Scripts/Editor/TMP_DropdownEditor.cs b/Scripts/Editor/TMP_DropdownEditor.cs index 48f8e2b..5e63fd8 100644 --- a/Scripts/Editor/TMP_DropdownEditor.cs +++ b/Scripts/Editor/TMP_DropdownEditor.cs @@ -17,6 +17,7 @@ public class DropdownEditor : SelectableEditor SerializedProperty m_ItemImage; SerializedProperty m_OnSelectionChanged; SerializedProperty m_Value; + SerializedProperty m_MultiSelect; SerializedProperty m_AlphaFadeSpeed; SerializedProperty m_Options; @@ -31,6 +32,7 @@ protected override void OnEnable() m_ItemImage = serializedObject.FindProperty("m_ItemImage"); m_OnSelectionChanged = serializedObject.FindProperty("m_OnValueChanged"); m_Value = serializedObject.FindProperty("m_Value"); + m_MultiSelect = serializedObject.FindProperty("m_MultiSelect"); m_AlphaFadeSpeed = serializedObject.FindProperty("m_AlphaFadeSpeed"); m_Options = serializedObject.FindProperty("m_Options"); } @@ -48,6 +50,7 @@ public override void OnInspectorGUI() EditorGUILayout.PropertyField(m_ItemText); EditorGUILayout.PropertyField(m_ItemImage); EditorGUILayout.PropertyField(m_Value); + EditorGUILayout.PropertyField(m_MultiSelect); EditorGUILayout.PropertyField(m_AlphaFadeSpeed); EditorGUILayout.PropertyField(m_Options); EditorGUILayout.PropertyField(m_OnSelectionChanged); diff --git a/Scripts/Runtime/TMP_EditorResourceManager.cs b/Scripts/Editor/TMP_EditorResourceManager.cs similarity index 53% rename from Scripts/Runtime/TMP_EditorResourceManager.cs rename to Scripts/Editor/TMP_EditorResourceManager.cs index 819bd66..6af79b2 100644 --- a/Scripts/Runtime/TMP_EditorResourceManager.cs +++ b/Scripts/Editor/TMP_EditorResourceManager.cs @@ -1,13 +1,47 @@ -#if UNITY_EDITOR - -using System.Collections.Generic; +using System.Collections.Generic; using UnityEngine; +using UnityEngine.Rendering; using UnityEditor; +using UnityEditor.TextCore.LowLevel; namespace TMPro { - public class TMP_EditorResourceManager + static class EditorEventCallbacks + { + [InitializeOnLoadMethod] + internal static void InitializeFontAssetResourceChangeCallBacks() + { + TMP_FontAsset.RegisterResourceForUpdate += TMP_EditorResourceManager.RegisterResourceForUpdate; + TMP_FontAsset.RegisterResourceForReimport += TMP_EditorResourceManager.RegisterResourceForReimport; + TMP_FontAsset.OnFontAssetTextureChanged += TMP_EditorResourceManager.AddTextureToAsset; + TMP_FontAsset.SetAtlasTextureIsReadable += FontEngineEditorUtilities.SetAtlasTextureIsReadable; + TMP_FontAsset.GetSourceFontRef += TMP_EditorResourceManager.GetSourceFontRef; + TMP_FontAsset.SetSourceFontGUID += TMP_EditorResourceManager.SetSourceFontGUID; + + // Callback to handle clearing dynamic font asset data when closing the Editor + EditorApplication.quitting += () => + { + // Find all font assets in the project + string searchPattern = "t:TMP_FontAsset"; + string[] fontAssetGUIDs = AssetDatabase.FindAssets(searchPattern); + + for (int i = 0; i < fontAssetGUIDs.Length; i++) + { + string fontAssetPath = AssetDatabase.GUIDToAssetPath(fontAssetGUIDs[i]); + TMP_FontAsset fontAsset = AssetDatabase.LoadAssetAtPath(fontAssetPath); + + if (fontAsset != null && (fontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic || fontAsset.atlasPopulationMode == AtlasPopulationMode.DynamicOS) && fontAsset.clearDynamicDataOnBuild && fontAsset.atlasTexture.width != 0) + { + Debug.Log("Clearing [" + fontAsset.name + "] dynamic font asset data."); + fontAsset.ClearFontAssetDataInternal(); + } + } + }; + } + } + + internal class TMP_EditorResourceManager { private static TMP_EditorResourceManager s_Instance; @@ -23,7 +57,7 @@ public class TMP_EditorResourceManager /// /// Get a singleton instance of the manager. /// - public static TMP_EditorResourceManager instance + internal static TMP_EditorResourceManager instance { get { @@ -39,7 +73,12 @@ public static TMP_EditorResourceManager instance /// private TMP_EditorResourceManager() { - Camera.onPostRender += OnCameraPostRender; + // Register to the appropriate callback for the given render pipeline. + if (RenderPipelineManager.currentPipeline == null) + Camera.onPostRender += OnCameraPostRender; + else + RenderPipelineManager.endFrameRendering += OnEndOfFrame; + Canvas.willRenderCanvases += OnPreRenderCanvases; } @@ -57,12 +96,21 @@ void OnPreRenderCanvases() DoPreRenderUpdates(); } + void OnEndOfFrame(ScriptableRenderContext renderContext, Camera[] cameras) + { + DoPostRenderUpdates(); + } + /// /// Register resource for re-import. /// /// internal static void RegisterResourceForReimport(Object obj) { + // Return if referenced object is not a persistent asset + if (!EditorUtility.IsPersistent(obj)) + return; + instance.InternalRegisterResourceForReimport(obj); } @@ -80,9 +128,13 @@ private void InternalRegisterResourceForReimport(Object obj) /// /// Register resource to be updated. /// - /// + /// internal static void RegisterResourceForUpdate(Object obj) { + // Return if referenced object is not a persistent asset + if (!EditorUtility.IsPersistent(obj)) + return; + instance.InternalRegisterResourceForUpdate(obj); } @@ -117,6 +169,44 @@ private void InternalRegisterFontAssetForDefinitionRefresh(TMP_FontAsset fontAss m_FontAssetDefinitionRefreshQueue.Add(fontAsset); } + /// + /// Add texture as sub asset to the referenced object. + /// + /// The texture to be added as sub object. + /// The object to which this texture sub object will be added. + internal static void AddTextureToAsset(Texture tex, Object obj) + { + // Return if referenced object is not a persistent asset + if (!EditorUtility.IsPersistent(obj)) + return; + + if (tex != null) + AssetDatabase.AddObjectToAsset(tex, obj); + + RegisterResourceForReimport(obj); + } + + /// + /// + /// + /// + /// + internal static Font GetSourceFontRef(string guid) + { + string path = AssetDatabase.GUIDToAssetPath(guid); + return AssetDatabase.LoadAssetAtPath(path); + } + + /// + /// + /// + /// + /// + internal static string SetSourceFontGUID(Font font) + { + return AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(font)); + } + void DoPostRenderUpdates() { // Handle objects that need updating @@ -127,7 +217,7 @@ void DoPostRenderUpdates() Object obj = m_ObjectUpdateQueue[i]; if (obj != null) { - EditorUtility.SetDirty(obj); + //EditorUtility.SetDirty(obj); } } @@ -148,7 +238,11 @@ void DoPostRenderUpdates() Object obj = m_ObjectReImportQueue[i]; if (obj != null) { - AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(obj)); + string assetPath = AssetDatabase.GetAssetPath(obj); + + // Exclude Assets not located in the project + if (assetPath.StartsWith("Assets/", System.StringComparison.OrdinalIgnoreCase)) + AssetDatabase.ImportAsset(assetPath); } } @@ -181,4 +275,3 @@ void DoPreRenderUpdates() } } } -#endif diff --git a/Scripts/Runtime/TMP_EditorResourceManager.cs.meta b/Scripts/Editor/TMP_EditorResourceManager.cs.meta similarity index 100% rename from Scripts/Runtime/TMP_EditorResourceManager.cs.meta rename to Scripts/Editor/TMP_EditorResourceManager.cs.meta diff --git a/Scripts/Editor/TMP_FontAssetEditor.cs b/Scripts/Editor/TMP_FontAssetEditor.cs index c963fa0..ffe9593 100644 --- a/Scripts/Editor/TMP_FontAssetEditor.cs +++ b/Scripts/Editor/TMP_FontAssetEditor.cs @@ -56,10 +56,14 @@ private struct UI_PanelState public static bool glyphTablePanel = false; public static bool characterTablePanel = false; public static bool fontFeatureTablePanel = false; + public static bool MarkToBaseTablePanel = false; + public static bool MarkToMarkTablePanel = false; } - private struct AtlasSettings + private struct GenerationSettings { + public Font sourceFont; + public int faceIndex; public GlyphRenderMode glyphRenderMode; public int pointSize; public int padding; @@ -120,10 +124,16 @@ private struct Warning private int m_CurrentGlyphPage = 0; private int m_CurrentCharacterPage = 0; private int m_CurrentKerningPage = 0; + private int m_CurrentMarkToBasePage = 0; + private int m_CurrentMarkToMarkPage = 0; private int m_SelectedGlyphRecord = -1; private int m_SelectedCharacterRecord = -1; private int m_SelectedAdjustmentRecord = -1; + private int m_SelectedMarkToBaseRecord = -1; + private int m_SelectedMarkToMarkRecord = -1; + + enum RecordSelectionType { CharacterRecord, GlyphRecord, AdjustmentPairRecord, MarkToBaseRecord, MarkToMarkRecord } private string m_dstGlyphID; private string m_dstUnicode; @@ -135,9 +145,13 @@ private struct Warning private Warning m_AddGlyphWarning; private Warning m_AddCharacterWarning; private bool m_DisplayDestructiveChangeWarning; - private AtlasSettings m_AtlasSettings; + private GenerationSettings m_GenerationSettings; private bool m_MaterialPresetsRequireUpdate; + private static readonly string[] k_InvalidFontFaces = { string.Empty }; + private string[] m_FontFaces; + private bool m_FaceInfoDirty; + private string m_GlyphSearchPattern; private List m_GlyphSearchList; @@ -147,6 +161,13 @@ private struct Warning private string m_KerningTableSearchPattern; private List m_KerningTableSearchList; + private string m_MarkToBaseTableSearchPattern; + private List m_MarkToBaseTableSearchList; + + private string m_MarkToMarkTableSearchPattern; + private List m_MarkToMarkTableSearchList; + + private bool m_isSearchDirty; private const string k_UndoRedo = "UndoRedoPerformed"; @@ -155,6 +176,7 @@ private struct Warning private SerializedProperty font_atlas_prop; private SerializedProperty font_material_prop; + private SerializedProperty m_FontFaceIndex_prop; private SerializedProperty m_AtlasRenderMode_prop; private SerializedProperty m_SamplingPointSize_prop; private SerializedProperty m_AtlasPadding_prop; @@ -184,6 +206,8 @@ private struct Warning private TMP_FontFeatureTable m_FontFeatureTable; private SerializedProperty m_FontFeatureTable_prop; private SerializedProperty m_GlyphPairAdjustmentRecords_prop; + private SerializedProperty m_MarkToBaseAdjustmentRecords_prop; + private SerializedProperty m_MarkToMarkAdjustmentRecords_prop; private TMP_SerializedPropertyHolder m_SerializedPropertyHolder; private SerializedProperty m_EmptyGlyphPairAdjustmentRecord_prop; @@ -193,6 +217,7 @@ private struct Warning private Material[] m_materialPresets; private bool isAssetDirty = false; + private bool m_IsFallbackGlyphCacheDirty; private int errorCode; @@ -204,8 +229,9 @@ public void OnEnable() m_FaceInfo_prop = serializedObject.FindProperty("m_FaceInfo"); font_atlas_prop = serializedObject.FindProperty("m_AtlasTextures").GetArrayElementAtIndex(0); - font_material_prop = serializedObject.FindProperty("material"); + font_material_prop = serializedObject.FindProperty("m_Material"); + m_FontFaceIndex_prop = m_FaceInfo_prop.FindPropertyRelative("m_FaceIndex"); m_AtlasPopulationMode_prop = serializedObject.FindProperty("m_AtlasPopulationMode"); m_AtlasRenderMode_prop = serializedObject.FindProperty("m_AtlasRenderMode"); m_SamplingPointSize_prop = m_FaceInfo_prop.FindPropertyRelative("m_PointSize"); @@ -231,6 +257,11 @@ public void OnEnable() EditorGUI.LabelField(rect, "Fallback List"); }; + m_list.onReorderCallback = itemList => + { + m_IsFallbackGlyphCacheDirty = true; + }; + // Clean up fallback list in the event if contains null elements. CleanFallbackFontAssetTable(); @@ -248,10 +279,15 @@ public void OnEnable() m_FontFeatureTable_prop = serializedObject.FindProperty("m_FontFeatureTable"); m_GlyphPairAdjustmentRecords_prop = m_FontFeatureTable_prop.FindPropertyRelative("m_GlyphPairAdjustmentRecords"); + m_MarkToBaseAdjustmentRecords_prop = m_FontFeatureTable_prop.FindPropertyRelative("m_MarkToBaseAdjustmentRecords"); + m_MarkToMarkAdjustmentRecords_prop = m_FontFeatureTable_prop.FindPropertyRelative("m_MarkToMarkAdjustmentRecords"); m_fontAsset = target as TMP_FontAsset; m_FontFeatureTable = m_fontAsset.fontFeatureTable; + // Get Font Faces and Styles + m_FontFaces = GetFontFaces(); + // Upgrade Font Feature Table if necessary if (m_fontAsset.m_KerningTable != null && m_fontAsset.m_KerningTable.kerningPairs != null && m_fontAsset.m_KerningTable.kerningPairs.Count > 0) m_fontAsset.ReadFontAssetDefinition(); @@ -278,7 +314,7 @@ public void OnDisable() if (m_DisplayDestructiveChangeWarning) { m_DisplayDestructiveChangeWarning = false; - RestoreAtlasGenerationSettings(); + RestoreGenerationSettings(); GUIUtility.keyboardControl = 0; serializedObject.ApplyModifiedProperties(); @@ -358,16 +394,28 @@ public override void OnInspectorGUI() EditorGUI.indentLevel = 1; EditorGUI.BeginChangeCheck(); - Font sourceFont = (Font)EditorGUILayout.ObjectField("Source Font File", m_fontAsset.m_SourceFontFile_EditorRef, typeof(Font), false); + Font sourceFont = (Font)EditorGUILayout.ObjectField("Source Font File", m_fontAsset.SourceFont_EditorRef, typeof(Font), false); if (EditorGUI.EndChangeCheck()) { - string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(sourceFont)); - m_fontAsset.m_SourceFontFileGUID = guid; - m_fontAsset.m_SourceFontFile_EditorRef = sourceFont; + m_GenerationSettings.sourceFont = m_fontAsset.SourceFont_EditorRef; + m_fontAsset.SourceFont_EditorRef = sourceFont; + m_FontFaces = GetFontFaces(0); + m_FaceInfoDirty = true; + m_DisplayDestructiveChangeWarning = true; + //m_MaterialPresetsRequireUpdate = true; } EditorGUI.BeginDisabledGroup(sourceFont == null); { + EditorGUI.BeginChangeCheck(); + m_FontFaceIndex_prop.intValue = EditorGUILayout.Popup(new GUIContent("Font Face"), m_FontFaceIndex_prop.intValue, m_FontFaces); + if (EditorGUI.EndChangeCheck()) + { + m_MaterialPresetsRequireUpdate = true; + m_DisplayDestructiveChangeWarning = true; + m_FaceInfoDirty = true; + } + EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_AtlasPopulationMode_prop, new GUIContent("Atlas Population Mode")); if (EditorGUI.EndChangeCheck()) @@ -376,6 +424,7 @@ public override void OnInspectorGUI() bool isDatabaseRefreshRequired = false; + // Static font asset if (m_AtlasPopulationMode_prop.intValue == 0) { m_fontAsset.sourceFontFile = null; @@ -386,16 +435,12 @@ public override void OnInspectorGUI() Texture2D tex = m_fontAsset.atlasTextures[i]; if (tex != null && tex.isReadable) - { - #if UNITY_2018_4_OR_NEWER && !UNITY_2018_4_0 && !UNITY_2018_4_1 && !UNITY_2018_4_2 && !UNITY_2018_4_3 && !UNITY_2018_4_4 - FontEngineEditorUtilities.SetAtlasTextureIsReadable(tex, false); - #endif - } + FontEngineEditorUtilities.SetAtlasTextureIsReadable(tex, false); } - Debug.Log("Atlas Population mode set to [Static]."); + //Debug.Log("Atlas Population mode set to [Static]."); } - else if (m_AtlasPopulationMode_prop.intValue == 1) + else // Dynamic font asset { if (m_fontAsset.m_SourceFontFile_EditorRef.dynamic == false) { @@ -414,15 +459,15 @@ public override void OnInspectorGUI() Texture2D tex = m_fontAsset.atlasTextures[i]; if (tex != null && tex.isReadable == false) - { - #if UNITY_2018_4_OR_NEWER && !UNITY_2018_4_0 && !UNITY_2018_4_1 && !UNITY_2018_4_2 && !UNITY_2018_4_3 && !UNITY_2018_4_4 - FontEngineEditorUtilities.SetAtlasTextureIsReadable(tex, true); - #endif - } + FontEngineEditorUtilities.SetAtlasTextureIsReadable(tex, true); } - Debug.Log("Atlas Population mode set to [Dynamic]."); + //Debug.Log("Atlas Population mode set to [" + (m_AtlasPopulationMode_prop.intValue == 1 ? "Dynamic" : "Dynamic OS") + "]."); } + + // Dynamic OS font asset + if (m_AtlasPopulationMode_prop.intValue == 2) + m_fontAsset.sourceFontFile = null; } if (isDatabaseRefreshRequired) @@ -435,7 +480,7 @@ public override void OnInspectorGUI() // Save state of atlas settings if (m_DisplayDestructiveChangeWarning == false) { - SavedAtlasGenerationSettings(); + SavedGenerationSettings(); //Undo.RegisterCompleteObjectUndo(m_fontAsset, "Font Asset Changes"); } @@ -467,6 +512,9 @@ public override void OnInspectorGUI() if (m_DisplayDestructiveChangeWarning) { + bool guiEnabledState = GUI.enabled; + GUI.enabled = true; + // These changes are destructive on the font asset rect = EditorGUILayout.GetControlRect(false, 60); rect.x += 15; @@ -477,11 +525,12 @@ public override void OnInspectorGUI() { m_DisplayDestructiveChangeWarning = false; - // Update face info is sampling point size was changed. - if (m_AtlasSettings.pointSize != m_SamplingPointSize_prop.intValue) + // Update face info if sampling point size was changed. + if (m_GenerationSettings.pointSize != m_SamplingPointSize_prop.intValue || m_FaceInfoDirty) { - FontEngine.LoadFontFace(m_fontAsset.sourceFontFile, m_SamplingPointSize_prop.intValue); + LoadFontFace(m_SamplingPointSize_prop.intValue, m_FontFaceIndex_prop.intValue); m_fontAsset.faceInfo = FontEngine.GetFaceInfo(); + m_FaceInfoDirty = false; } Material mat = m_fontAsset.material; @@ -525,12 +574,14 @@ public override void OnInspectorGUI() if (GUI.Button(new Rect(rect.width - 56, rect.y + 36, 80, 18), new GUIContent("Revert"))) { m_DisplayDestructiveChangeWarning = false; - RestoreAtlasGenerationSettings(); + RestoreGenerationSettings(); GUIUtility.keyboardControl = 0; // TODO: Clear undo buffers. //Undo.ClearUndo(m_fontAsset); } + + GUI.enabled = guiEnabledState; } } EditorGUI.EndDisabledGroup(); @@ -671,7 +722,6 @@ public override void OnInspectorGUI() { EditorGUIUtility.labelWidth = 120; EditorGUI.indentLevel = 0; - m_list.DoLayoutList(); EditorGUILayout.Space(); } @@ -785,6 +835,9 @@ public override void OnInspectorGUI() // Draw Selection Highlight and Glyph Options if (m_SelectedCharacterRecord == i) { + // Reset other selections + ResetSelections(RecordSelectionType.CharacterRecord); + TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255)); // Draw Glyph management options @@ -984,6 +1037,9 @@ public override void OnInspectorGUI() // Draw Selection Highlight and Glyph Options if (m_SelectedGlyphRecord == i) { + // Reset other selections + ResetSelections(RecordSelectionType.GlyphRecord); + TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255)); // Draw Glyph management options @@ -1179,6 +1235,9 @@ public override void OnInspectorGUI() // Draw Selection Highlight and Kerning Pair Options if (m_SelectedAdjustmentRecord == i) { + // Reset other selections + ResetSelections(RecordSelectionType.AdjustmentPairRecord); + TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255)); // Draw Glyph management options @@ -1229,11 +1288,11 @@ public override void OnInspectorGUI() errorCode = -1; uint pairKey = secondGlyphIndex << 16 | firstGlyphIndex; - if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.ContainsKey(pairKey) == false) + if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.ContainsKey(pairKey) == false) { TMP_GlyphPairAdjustmentRecord adjustmentRecord = new TMP_GlyphPairAdjustmentRecord(new TMP_GlyphAdjustmentRecord(firstGlyphIndex, firstValueRecord), new TMP_GlyphAdjustmentRecord(secondGlyphIndex, secondValueRecord)); m_FontFeatureTable.m_GlyphPairAdjustmentRecords.Add(adjustmentRecord); - m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Add(pairKey, adjustmentRecord); + m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.Add(pairKey, adjustmentRecord); errorCode = 0; } @@ -1279,11 +1338,313 @@ public override void OnInspectorGUI() } #endregion - if (serializedObject.ApplyModifiedProperties() || evt_cmd == k_UndoRedo || isAssetDirty) + // MARK TO BASE Font Feature Table + #region MARK TO BASE + EditorGUIUtility.labelWidth = labelWidth; + EditorGUIUtility.fieldWidth = fieldWidth; + EditorGUI.indentLevel = 0; + rect = EditorGUILayout.GetControlRect(false, 24); + + int markToBaseAdjustmentRecordCount = m_fontAsset.fontFeatureTable.MarkToBaseAdjustmentRecords.Count; + + if (GUI.Button(rect, new GUIContent("Mark To Base Adjustment Table [" + markToBaseAdjustmentRecordCount + "]" + (rect.width > 340 ? " Records" : ""), "List of Mark to Base adjustment records."), TMP_UIStyleManager.sectionHeader)) + UI_PanelState.MarkToBaseTablePanel = !UI_PanelState.MarkToBaseTablePanel; + + GUI.Label(rect, (UI_PanelState.MarkToBaseTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel); + + if (UI_PanelState.MarkToBaseTablePanel) + { + int arraySize = m_MarkToBaseAdjustmentRecords_prop.arraySize; + int itemsPerPage = 20; + + // Display Mark Adjust Records Management Tools + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + { + // Search Bar implementation + #region DISPLAY SEARCH BAR + EditorGUILayout.BeginHorizontal(); + { + EditorGUIUtility.labelWidth = 150f; + EditorGUI.BeginChangeCheck(); + string searchPattern = EditorGUILayout.TextField("Mark to Base Search", m_MarkToBaseTableSearchPattern, "SearchTextField"); + if (EditorGUI.EndChangeCheck() || m_isSearchDirty) + { + if (string.IsNullOrEmpty(searchPattern) == false) + { + m_MarkToBaseTableSearchPattern = searchPattern; + + // Search Glyph Table for potential matches + SearchMarkToBaseTable(m_MarkToBaseTableSearchPattern, ref m_MarkToBaseTableSearchList); + } + else + m_MarkToBaseTableSearchPattern = null; + + m_isSearchDirty = false; + } + + string styleName = string.IsNullOrEmpty(m_MarkToBaseTableSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton"; + if (GUILayout.Button(GUIContent.none, styleName)) + { + GUIUtility.keyboardControl = 0; + m_MarkToBaseTableSearchPattern = string.Empty; + } + } + EditorGUILayout.EndHorizontal(); + #endregion + + // Display Page Navigation + if (!string.IsNullOrEmpty(m_MarkToBaseTableSearchPattern)) + arraySize = m_MarkToBaseTableSearchList.Count; + + DisplayPageNavigation(ref m_CurrentKerningPage, arraySize, itemsPerPage); + } + EditorGUILayout.EndVertical(); + + if (arraySize > 0) + { + // Display each GlyphInfo entry using the GlyphInfo property drawer. + for (int i = itemsPerPage * m_CurrentMarkToBasePage; i < arraySize && i < itemsPerPage * (m_CurrentMarkToBasePage + 1); i++) + { + // Define the start of the selection region of the element. + Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)); + + int elementIndex = i; + if (!string.IsNullOrEmpty(m_MarkToBaseTableSearchPattern)) + elementIndex = m_MarkToBaseTableSearchList[i]; + + SerializedProperty markToBasePropertyRecord = m_MarkToBaseAdjustmentRecords_prop.GetArrayElementAtIndex(elementIndex); + + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + + using (new EditorGUI.DisabledScope(i != m_SelectedMarkToBaseRecord)) + { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(markToBasePropertyRecord, new GUIContent("Selectable")); + + if (EditorGUI.EndChangeCheck()) + { + UpdateMarkToBaseAdjustmentRecordLookup(markToBasePropertyRecord); + } + } + + EditorGUILayout.EndVertical(); + + // Define the end of the selection region of the element. + Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)); + + // Check for Item selection + Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y); + if (DoSelectionCheck(selectionArea)) + { + if (m_SelectedMarkToBaseRecord == i) + { + m_SelectedMarkToBaseRecord = -1; + } + else + { + m_SelectedMarkToBaseRecord = i; + GUIUtility.keyboardControl = 0; + } + } + + // Draw Selection Highlight and Kerning Pair Options + if (m_SelectedMarkToBaseRecord == i) + { + // Reset other selections + ResetSelections(RecordSelectionType.MarkToBaseRecord); + + TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255)); + + // Draw Glyph management options + Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f); + float optionAreaWidth = controlRect.width; + float btnWidth = optionAreaWidth / 4; + + Rect position = new Rect(controlRect.x + controlRect.width - btnWidth, controlRect.y, btnWidth, controlRect.height); + + // Remove Mark to Base Record + GUI.enabled = true; + if (GUI.Button(position, "Remove")) + { + GUIUtility.keyboardControl = 0; + + RemoveAdjustmentRecord(m_MarkToBaseAdjustmentRecords_prop, i); + + isAssetDirty = true; + m_SelectedMarkToBaseRecord = -1; + m_isSearchDirty = true; + break; + } + } + } + } + + DisplayAddRemoveButtons(m_MarkToBaseAdjustmentRecords_prop, m_SelectedMarkToBaseRecord, markToBaseAdjustmentRecordCount); + + DisplayPageNavigation(ref m_CurrentMarkToBasePage, arraySize, itemsPerPage); + + GUILayout.Space(5); + } + #endregion + + // MARK TO MARK Font Feature Table + #region MARK TO MARK + EditorGUIUtility.labelWidth = labelWidth; + EditorGUIUtility.fieldWidth = fieldWidth; + EditorGUI.indentLevel = 0; + rect = EditorGUILayout.GetControlRect(false, 24); + + int markToMarkAdjustmentRecordCount = m_fontAsset.fontFeatureTable.MarkToMarkAdjustmentRecords.Count; + + if (GUI.Button(rect, new GUIContent("Mark To Mark Adjustment Table [" + markToMarkAdjustmentRecordCount + "]" + (rect.width > 340 ? " Records" : ""), "List of Mark to Mark adjustment records."), TMP_UIStyleManager.sectionHeader)) + UI_PanelState.MarkToMarkTablePanel = !UI_PanelState.MarkToMarkTablePanel; + + GUI.Label(rect, (UI_PanelState.MarkToMarkTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel); + + if (UI_PanelState.MarkToMarkTablePanel) + { + int arraySize = m_MarkToMarkAdjustmentRecords_prop.arraySize; + int itemsPerPage = 20; + + // Display Kerning Pair Management Tools + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + { + // Search Bar implementation + #region DISPLAY SEARCH BAR + EditorGUILayout.BeginHorizontal(); + { + EditorGUIUtility.labelWidth = 150f; + EditorGUI.BeginChangeCheck(); + string searchPattern = EditorGUILayout.TextField("Mark to Mark Search", m_MarkToMarkTableSearchPattern, "SearchTextField"); + if (EditorGUI.EndChangeCheck() || m_isSearchDirty) + { + if (string.IsNullOrEmpty(searchPattern) == false) + { + m_MarkToMarkTableSearchPattern = searchPattern; + + // Search Glyph Table for potential matches + SearchMarkToMarkTable(m_MarkToMarkTableSearchPattern, ref m_MarkToMarkTableSearchList); + } + else + m_MarkToMarkTableSearchPattern = null; + + m_isSearchDirty = false; + } + + string styleName = string.IsNullOrEmpty(m_MarkToMarkTableSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton"; + if (GUILayout.Button(GUIContent.none, styleName)) + { + GUIUtility.keyboardControl = 0; + m_MarkToMarkTableSearchPattern = string.Empty; + } + } + EditorGUILayout.EndHorizontal(); + #endregion + + // Display Page Navigation + if (!string.IsNullOrEmpty(m_MarkToMarkTableSearchPattern)) + arraySize = m_MarkToMarkTableSearchList.Count; + + DisplayPageNavigation(ref m_CurrentMarkToMarkPage, arraySize, itemsPerPage); + } + EditorGUILayout.EndVertical(); + + if (arraySize > 0) + { + // Display each GlyphInfo entry using the GlyphInfo property drawer. + for (int i = itemsPerPage * m_CurrentMarkToMarkPage; i < arraySize && i < itemsPerPage * (m_CurrentMarkToMarkPage + 1); i++) + { + // Define the start of the selection region of the element. + Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)); + + int elementIndex = i; + if (!string.IsNullOrEmpty(m_MarkToMarkTableSearchPattern)) + elementIndex = m_MarkToMarkTableSearchList[i]; + + SerializedProperty markToMarkPropertyRecord = m_MarkToMarkAdjustmentRecords_prop.GetArrayElementAtIndex(elementIndex); + + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + + using (new EditorGUI.DisabledScope(i != m_SelectedMarkToMarkRecord)) + { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(markToMarkPropertyRecord, new GUIContent("Selectable")); + + if (EditorGUI.EndChangeCheck()) + { + UpdateMarkToMarkAdjustmentRecordLookup(markToMarkPropertyRecord); + } + } + + EditorGUILayout.EndVertical(); + + // Define the end of the selection region of the element. + Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)); + + // Check for Item selection + Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y); + if (DoSelectionCheck(selectionArea)) + { + if (m_SelectedMarkToMarkRecord == i) + { + m_SelectedMarkToMarkRecord = -1; + } + else + { + m_SelectedMarkToMarkRecord = i; + GUIUtility.keyboardControl = 0; + } + } + + // Draw Selection Highlight and Kerning Pair Options + if (m_SelectedMarkToMarkRecord == i) + { + // Reset other selections + ResetSelections(RecordSelectionType.MarkToMarkRecord); + + TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255)); + + // Draw Glyph management options + Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f); + float optionAreaWidth = controlRect.width; + float btnWidth = optionAreaWidth / 4; + + Rect position = new Rect(controlRect.x + controlRect.width - btnWidth, controlRect.y, btnWidth, controlRect.height); + + // Remove Mark to Base Record + GUI.enabled = true; + if (GUI.Button(position, "Remove")) + { + GUIUtility.keyboardControl = 0; + + RemoveAdjustmentRecord(m_MarkToMarkAdjustmentRecords_prop, i); + + isAssetDirty = true; + m_SelectedMarkToMarkRecord = -1; + m_isSearchDirty = true; + break; + } + } + } + } + + DisplayAddRemoveButtons(m_MarkToMarkAdjustmentRecords_prop, m_SelectedMarkToMarkRecord, markToMarkAdjustmentRecordCount); + + DisplayPageNavigation(ref m_CurrentMarkToMarkPage, arraySize, itemsPerPage); + + GUILayout.Space(5); + } + #endregion + + if (serializedObject.ApplyModifiedProperties() || evt_cmd == k_UndoRedo || isAssetDirty || m_IsFallbackGlyphCacheDirty) { // Delay callback until user has decided to Apply or Revert the changes. if (m_DisplayDestructiveChangeWarning == false) + { + TMP_ResourceManager.RebuildFontAssetCache(); TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, m_fontAsset); + m_IsFallbackGlyphCacheDirty = false; + } if (m_fontAsset.IsFontAssetLookupTablesDirty || evt_cmd == k_UndoRedo) m_fontAsset.ReadFontAssetDefinition(); @@ -1296,10 +1657,93 @@ public override void OnInspectorGUI() // Clear selection if mouse event was not consumed. GUI.enabled = true; if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0) + { m_SelectedAdjustmentRecord = -1; + m_SelectedMarkToBaseRecord = -1; + m_SelectedMarkToMarkRecord = -1; + } + + } + + public override bool HasPreviewGUI() + { + return true; + } + + public override void OnPreviewGUI(Rect rect, GUIStyle background) + { + if (m_SelectedMarkToBaseRecord != -1) + DrawMarkToBasePreview(m_SelectedMarkToBaseRecord, rect); + + if (m_SelectedMarkToMarkRecord != -1) + DrawMarkToMarkPreview(m_SelectedMarkToMarkRecord, rect); + + } + + void ResetSelections(RecordSelectionType type) + { + switch (type) + { + case RecordSelectionType.CharacterRecord: + m_SelectedGlyphRecord = -1; + m_SelectedAdjustmentRecord = -1; + m_SelectedMarkToBaseRecord = -1; + m_SelectedMarkToMarkRecord = -1; + break; + case RecordSelectionType.GlyphRecord: + m_SelectedCharacterRecord = -1; + m_SelectedAdjustmentRecord = -1; + m_SelectedMarkToBaseRecord = -1; + m_SelectedMarkToMarkRecord = -1; + break; + case RecordSelectionType.AdjustmentPairRecord: + m_SelectedCharacterRecord = -1; + m_SelectedGlyphRecord = -1; + m_SelectedMarkToBaseRecord = -1; + m_SelectedMarkToMarkRecord = -1; + break; + case RecordSelectionType.MarkToBaseRecord: + m_SelectedCharacterRecord = -1; + m_SelectedGlyphRecord = -1; + m_SelectedAdjustmentRecord = -1; + m_SelectedMarkToMarkRecord = -1; + break; + case RecordSelectionType.MarkToMarkRecord: + m_SelectedCharacterRecord = -1; + m_SelectedGlyphRecord = -1; + m_SelectedAdjustmentRecord = -1; + m_SelectedMarkToBaseRecord = -1; + break; + } + } + + string[] GetFontFaces() + { + return GetFontFaces(m_FontFaceIndex_prop.intValue); + } + + string[] GetFontFaces(int faceIndex) + { + if (LoadFontFace(m_SamplingPointSize_prop.intValue, faceIndex) == FontEngineError.Success) + return FontEngine.GetFontFaces(); + + return k_InvalidFontFaces; } + FontEngineError LoadFontFace(int pointSize, int faceIndex) + { + if (m_fontAsset.SourceFont_EditorRef != null) + { + if (FontEngine.LoadFontFace(m_fontAsset.SourceFont_EditorRef, pointSize, faceIndex) == FontEngineError.Success) + return FontEngineError.Success; + } + + // Requires Unity 2018.4.35f1 + return FontEngine.LoadFontFace(m_fontAsset.faceInfo.familyName, m_fontAsset.faceInfo.styleName, pointSize); + } + + void CleanFallbackFontAssetTable() { SerializedProperty m_FallbackFontAsseTable = serializedObject.FindProperty("m_FallbackFontAssetTable"); @@ -1328,27 +1772,34 @@ void CleanFallbackFontAssetTable() } } - void SavedAtlasGenerationSettings() + + void SavedGenerationSettings() { - m_AtlasSettings.glyphRenderMode = (GlyphRenderMode)m_AtlasRenderMode_prop.intValue; - m_AtlasSettings.pointSize = m_SamplingPointSize_prop.intValue; - m_AtlasSettings.padding = m_AtlasPadding_prop.intValue; - m_AtlasSettings.atlasWidth = m_AtlasWidth_prop.intValue; - m_AtlasSettings.atlasHeight = m_AtlasHeight_prop.intValue; + m_GenerationSettings.faceIndex = m_FontFaceIndex_prop.intValue; + m_GenerationSettings.glyphRenderMode = (GlyphRenderMode)m_AtlasRenderMode_prop.intValue; + m_GenerationSettings.pointSize = m_SamplingPointSize_prop.intValue; + m_GenerationSettings.padding = m_AtlasPadding_prop.intValue; + m_GenerationSettings.atlasWidth = m_AtlasWidth_prop.intValue; + m_GenerationSettings.atlasHeight = m_AtlasHeight_prop.intValue; } - void RestoreAtlasGenerationSettings() + void RestoreGenerationSettings() { - m_AtlasRenderMode_prop.intValue = (int)m_AtlasSettings.glyphRenderMode; - m_SamplingPointSize_prop.intValue = m_AtlasSettings.pointSize; - m_AtlasPadding_prop.intValue = m_AtlasSettings.padding; - m_AtlasWidth_prop.intValue = m_AtlasSettings.atlasWidth; - m_AtlasHeight_prop.intValue = m_AtlasSettings.atlasHeight; + m_fontAsset.SourceFont_EditorRef = m_GenerationSettings.sourceFont; + m_FontFaceIndex_prop.intValue = m_GenerationSettings.faceIndex; + m_SamplingPointSize_prop.intValue = m_GenerationSettings.pointSize; + m_FontFaces = GetFontFaces(); + + m_AtlasRenderMode_prop.intValue = (int)m_GenerationSettings.glyphRenderMode; + m_AtlasPadding_prop.intValue = m_GenerationSettings.padding; + m_AtlasWidth_prop.intValue = m_GenerationSettings.atlasWidth; + m_AtlasHeight_prop.intValue = m_GenerationSettings.atlasHeight; } void UpdateFontAssetCreationSettings() { + m_fontAsset.m_CreationSettings.faceIndex = m_FontFaceIndex_prop.intValue; m_fontAsset.m_CreationSettings.pointSize = m_SamplingPointSize_prop.intValue; m_fontAsset.m_CreationSettings.renderMode = m_AtlasRenderMode_prop.intValue; m_fontAsset.m_CreationSettings.padding = m_AtlasPadding_prop.intValue; @@ -1395,6 +1846,52 @@ void UpdateGlyphData(SerializedProperty property, int index) } + void DisplayAddRemoveButtons(SerializedProperty property, int selectedRecord, int recordCount) + { + Rect rect = EditorGUILayout.GetControlRect(false, 20); + + rect.width /= 6; + // Add Style + rect.x = rect.width * 4 + 15; + if (GUI.Button(rect, "+")) + { + int index = selectedRecord == -1 ? 0 : selectedRecord; + + if (index > recordCount) + index = recordCount; + + // Copy selected element + property.InsertArrayElementAtIndex(index); + + // Select newly inserted element + selectedRecord = index + 1; + + serializedObject.ApplyModifiedProperties(); + + m_fontAsset.ReadFontAssetDefinition(); + + } + + // Delete style + rect.x += rect.width; + if (selectedRecord == -1 || selectedRecord >= recordCount) GUI.enabled = false; + if (GUI.Button(rect, "-")) + { + int index = selectedRecord == -1 ? 0 : selectedRecord; + + property.DeleteArrayElementAtIndex(index); + + selectedRecord = -1; + serializedObject.ApplyModifiedProperties(); + + m_fontAsset.ReadFontAssetDefinition(); + return; + } + + GUI.enabled = true; + } + + void DisplayPageNavigation(ref int currentPage, int arraySize, int itemsPerPage) { Rect pagePos = EditorGUILayout.GetControlRect(false, 20); @@ -1582,6 +2079,92 @@ void RemoveAdjustmentPairFromList(int index) m_fontAsset.ReadFontAssetDefinition(); } + private void UpdateMarkToBaseAdjustmentRecordLookup(SerializedProperty property) + { + MarkToBaseAdjustmentRecord adjustmentRecord = GetMarkToBaseAdjustmentRecord(property); + + uint firstGlyphIndex = adjustmentRecord.baseGlyphID; + uint secondGlyphIndex = adjustmentRecord.markGlyphID; + + uint key = secondGlyphIndex << 16 | firstGlyphIndex; + + // Lookup dictionary entry and update it + if (m_FontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.ContainsKey(key)) + m_FontFeatureTable.m_MarkToBaseAdjustmentRecordLookup[key] = adjustmentRecord; + } + + MarkToBaseAdjustmentRecord GetMarkToBaseAdjustmentRecord(SerializedProperty property) + { + MarkToBaseAdjustmentRecord adjustmentRecord = new MarkToBaseAdjustmentRecord(); + + adjustmentRecord.baseGlyphID = (uint)property.FindPropertyRelative("m_BaseGlyphID").intValue; + SerializedProperty baseAnchorPointProperty = property.FindPropertyRelative("m_BaseGlyphAnchorPoint"); + + GlyphAnchorPoint baseAnchorPoint = new GlyphAnchorPoint(); + baseAnchorPoint.xCoordinate = baseAnchorPointProperty.FindPropertyRelative("m_XCoordinate").floatValue; + baseAnchorPoint.yCoordinate = baseAnchorPointProperty.FindPropertyRelative("m_YCoordinate").floatValue; + adjustmentRecord.baseGlyphAnchorPoint = baseAnchorPoint; + + adjustmentRecord.markGlyphID = (uint)property.FindPropertyRelative("m_MarkGlyphID").intValue; + SerializedProperty markAdjustmentRecordProperty = property.FindPropertyRelative("m_MarkPositionAdjustment"); + + MarkPositionAdjustment markAdjustmentRecord = new MarkPositionAdjustment(); + markAdjustmentRecord.xPositionAdjustment = markAdjustmentRecordProperty.FindPropertyRelative("m_XPositionAdjustment").floatValue; + markAdjustmentRecord.yPositionAdjustment = markAdjustmentRecordProperty.FindPropertyRelative("m_YPositionAdjustment").floatValue; + adjustmentRecord.markPositionAdjustment = markAdjustmentRecord; + + return adjustmentRecord; + } + + private void UpdateMarkToMarkAdjustmentRecordLookup(SerializedProperty property) + { + MarkToMarkAdjustmentRecord adjustmentRecord = GetMarkToMarkAdjustmentRecord(property); + + uint firstGlyphIndex = adjustmentRecord.baseMarkGlyphID; + uint secondGlyphIndex = adjustmentRecord.combiningMarkGlyphID; + + uint key = secondGlyphIndex << 16 | firstGlyphIndex; + + // Lookup dictionary entry and update it + if (m_FontFeatureTable.m_MarkToMarkAdjustmentRecordLookup.ContainsKey(key)) + m_FontFeatureTable.m_MarkToMarkAdjustmentRecordLookup[key] = adjustmentRecord; + } + + MarkToMarkAdjustmentRecord GetMarkToMarkAdjustmentRecord(SerializedProperty property) + { + MarkToMarkAdjustmentRecord adjustmentRecord = new MarkToMarkAdjustmentRecord(); + + adjustmentRecord.baseMarkGlyphID = (uint)property.FindPropertyRelative("m_BaseMarkGlyphID").intValue; + SerializedProperty baseAnchorPointProperty = property.FindPropertyRelative("m_BaseMarkGlyphAnchorPoint"); + + GlyphAnchorPoint baseAnchorPoint = new GlyphAnchorPoint(); + baseAnchorPoint.xCoordinate = baseAnchorPointProperty.FindPropertyRelative("m_XCoordinate").floatValue; + baseAnchorPoint.yCoordinate = baseAnchorPointProperty.FindPropertyRelative("m_YCoordinate").floatValue; + adjustmentRecord.baseMarkGlyphAnchorPoint = baseAnchorPoint; + + adjustmentRecord.combiningMarkGlyphID = (uint)property.FindPropertyRelative("m_CombiningMarkGlyphID").intValue; + SerializedProperty markAdjustmentRecordProperty = property.FindPropertyRelative("m_CombiningMarkPositionAdjustment"); + + MarkPositionAdjustment markAdjustment = new MarkPositionAdjustment(); + markAdjustment.xPositionAdjustment = markAdjustmentRecordProperty.FindPropertyRelative("m_XPositionAdjustment").floatValue; + markAdjustment.yPositionAdjustment = markAdjustmentRecordProperty.FindPropertyRelative("m_YPositionAdjustment").floatValue; + adjustmentRecord.combiningMarkPositionAdjustment = markAdjustment; + + return adjustmentRecord; + } + + void RemoveAdjustmentRecord(SerializedProperty property, int index) + { + if (index > property.arraySize) + return; + + property.DeleteArrayElementAtIndex(index); + + serializedObject.ApplyModifiedProperties(); + + m_fontAsset.ReadFontAssetDefinition(); + } + /// /// /// @@ -1735,5 +2318,254 @@ void SearchKerningTable(string searchPattern, ref List searchResults) searchResults.Add(i); } } + + void SearchMarkToBaseTable(string searchPattern, ref List searchResults) + { + if (searchResults == null) searchResults = new List(); + + searchResults.Clear(); + + // Lookup glyph index of potential characters contained in the search pattern. + uint firstGlyphIndex = 0; + TMP_Character firstCharacterSearch; + + if (searchPattern.Length > 0 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[0], out firstCharacterSearch)) + firstGlyphIndex = firstCharacterSearch.glyphIndex; + + uint secondGlyphIndex = 0; + TMP_Character secondCharacterSearch; + + if (searchPattern.Length > 1 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[1], out secondCharacterSearch)) + secondGlyphIndex = secondCharacterSearch.glyphIndex; + + int arraySize = m_MarkToBaseAdjustmentRecords_prop.arraySize; + + for (int i = 0; i < arraySize; i++) + { + SerializedProperty record = m_MarkToBaseAdjustmentRecords_prop.GetArrayElementAtIndex(i); + + int baseGlyphIndex = record.FindPropertyRelative("m_BaseGlyphID").intValue; + int markGlyphIndex = record.FindPropertyRelative("m_MarkGlyphID").intValue; + + if (firstGlyphIndex == baseGlyphIndex && secondGlyphIndex == markGlyphIndex) + searchResults.Add(i); + else if (searchPattern.Length == 1 && (firstGlyphIndex == baseGlyphIndex || firstGlyphIndex == markGlyphIndex)) + searchResults.Add(i); + else if (baseGlyphIndex.ToString().Contains(searchPattern)) + searchResults.Add(i); + else if (markGlyphIndex.ToString().Contains(searchPattern)) + searchResults.Add(i); + } + } + + void SearchMarkToMarkTable(string searchPattern, ref List searchResults) + { + if (searchResults == null) searchResults = new List(); + + searchResults.Clear(); + + // Lookup glyph index of potential characters contained in the search pattern. + uint firstGlyphIndex = 0; + TMP_Character firstCharacterSearch; + + if (searchPattern.Length > 0 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[0], out firstCharacterSearch)) + firstGlyphIndex = firstCharacterSearch.glyphIndex; + + uint secondGlyphIndex = 0; + TMP_Character secondCharacterSearch; + + if (searchPattern.Length > 1 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[1], out secondCharacterSearch)) + secondGlyphIndex = secondCharacterSearch.glyphIndex; + + int arraySize = m_MarkToMarkAdjustmentRecords_prop.arraySize; + + for (int i = 0; i < arraySize; i++) + { + SerializedProperty record = m_MarkToMarkAdjustmentRecords_prop.GetArrayElementAtIndex(i); + + int baseGlyphIndex = record.FindPropertyRelative("m_BaseMarkGlyphID").intValue; + int markGlyphIndex = record.FindPropertyRelative("m_CombiningMarkGlyphID").intValue; + + if (firstGlyphIndex == baseGlyphIndex && secondGlyphIndex == markGlyphIndex) + searchResults.Add(i); + else if (searchPattern.Length == 1 && (firstGlyphIndex == baseGlyphIndex || firstGlyphIndex == markGlyphIndex)) + searchResults.Add(i); + else if (baseGlyphIndex.ToString().Contains(searchPattern)) + searchResults.Add(i); + else if (markGlyphIndex.ToString().Contains(searchPattern)) + searchResults.Add(i); + } + } + + void DrawMarkToBasePreview(int selectedRecord, Rect rect) + { + MarkToBaseAdjustmentRecord adjustmentRecord = m_fontAsset.fontFeatureTable.m_MarkToBaseAdjustmentRecords[selectedRecord]; + + uint baseGlyphIndex = adjustmentRecord.baseGlyphID; + uint markGlyphIndex = adjustmentRecord.markGlyphID; + + if (baseGlyphIndex == 0 || markGlyphIndex == 0) + return; + + float lineHeight = m_fontAsset.faceInfo.ascentLine - m_fontAsset.faceInfo.descentLine; + float scale = rect.width < rect.height ? rect.width / lineHeight : rect.height / lineHeight; + scale *= 0.9f; + + Glyph baseGlyph; + m_fontAsset.glyphLookupTable.TryGetValue(baseGlyphIndex, out baseGlyph); + + if (baseGlyph == null) + return; + + Rect center = new Rect(rect.x + rect.width / 2, rect.y + rect.height / 2, rect.width, rect.height); + + Vector2 origin = new Vector2(center.x, center.y); + origin.x = origin.x - (baseGlyph.metrics.horizontalBearingX + baseGlyph.metrics.width / 2) * scale; + origin.y = origin.y + (baseGlyph.metrics.horizontalBearingY - baseGlyph.metrics.height / 2) * scale; + + // Draw Baseline + DrawBaseline(origin, rect.width, Color.grey); + + // Draw Origin + DrawAnchorPoint(origin, Color.yellow); + + Rect baseGlyphPosition = new Rect(origin.x + baseGlyph.metrics.horizontalBearingX * scale, origin.y - baseGlyph.metrics.horizontalBearingY * scale , rect.width, rect.height); + + DrawGlyph(baseGlyph, baseGlyphPosition, scale); + + Vector2 baseAnchorPosition = new Vector2(origin.x + adjustmentRecord.baseGlyphAnchorPoint.xCoordinate * scale, origin.y - adjustmentRecord.baseGlyphAnchorPoint.yCoordinate * scale); + + DrawAnchorPoint(baseAnchorPosition, Color.green); + + Glyph markGlyph = m_fontAsset.glyphLookupTable[markGlyphIndex]; + + Rect markGlyphPosition = new Rect(baseAnchorPosition.x + (markGlyph.metrics.horizontalBearingX - adjustmentRecord.markPositionAdjustment.xPositionAdjustment) * scale, baseAnchorPosition.y + (adjustmentRecord.markPositionAdjustment.yPositionAdjustment - markGlyph.metrics.horizontalBearingY) * scale, markGlyph.metrics.width, markGlyph.metrics.height); + + // Draw Mark Origin + DrawGlyph(markGlyph, markGlyphPosition, scale); + } + + void DrawMarkToMarkPreview(int selectedRecord, Rect rect) + { + MarkToMarkAdjustmentRecord adjustmentRecord = m_fontAsset.fontFeatureTable.m_MarkToMarkAdjustmentRecords[selectedRecord]; + + uint baseGlyphIndex = adjustmentRecord.baseMarkGlyphID; + uint markGlyphIndex = adjustmentRecord.combiningMarkGlyphID; + + if (baseGlyphIndex == 0 || markGlyphIndex == 0) + return; + + float lineHeight = m_fontAsset.faceInfo.ascentLine - m_fontAsset.faceInfo.descentLine; + float scale = rect.width < rect.height ? rect.width / lineHeight : rect.height / lineHeight; + scale *= 0.9f; + + Glyph baseGlyph; + m_fontAsset.glyphLookupTable.TryGetValue(baseGlyphIndex, out baseGlyph); + + if (baseGlyph == null) + return; + + Rect center = new Rect(rect.x + rect.width / 2, rect.y + rect.height / 2, rect.width, rect.height); + + Vector2 origin = new Vector2(center.x, center.y); + origin.x = origin.x - (baseGlyph.metrics.horizontalBearingX + baseGlyph.metrics.width / 2) * scale; + origin.y = origin.y + (baseGlyph.metrics.horizontalBearingY - baseGlyph.metrics.height / 2) * scale; + + // Draw Baseline + DrawBaseline(origin, rect.width, Color.grey); + + // Draw Origin + DrawAnchorPoint(origin, Color.yellow); + + Rect baseGlyphPosition = new Rect(origin.x + baseGlyph.metrics.horizontalBearingX * scale, origin.y - baseGlyph.metrics.horizontalBearingY * scale , rect.width, rect.height); + + DrawGlyph(baseGlyph, baseGlyphPosition, scale); + + Vector2 baseAnchorPosition = new Vector2(origin.x + adjustmentRecord.baseMarkGlyphAnchorPoint.xCoordinate * scale, origin.y - adjustmentRecord.baseMarkGlyphAnchorPoint.yCoordinate * scale); + + DrawAnchorPoint(baseAnchorPosition, Color.green); + + Glyph markGlyph = m_fontAsset.glyphLookupTable[markGlyphIndex]; + + Rect markGlyphPosition = new Rect(baseAnchorPosition.x + (markGlyph.metrics.horizontalBearingX - adjustmentRecord.combiningMarkPositionAdjustment.xPositionAdjustment) * scale, baseAnchorPosition.y + (adjustmentRecord.combiningMarkPositionAdjustment.yPositionAdjustment - markGlyph.metrics.horizontalBearingY) * scale, markGlyph.metrics.width, markGlyph.metrics.height); + + DrawGlyph(markGlyph, markGlyphPosition, scale); + } + + void DrawBaseline(Vector2 position, float width, Color color) + { + Handles.color = color; + + // Horizontal line + Handles.DrawLine(new Vector2(0f, position.y), new Vector2(width, position.y)); + } + + void DrawAnchorPoint(Vector2 position, Color color) + { + Handles.color = color; + + // Horizontal line + Handles.DrawLine(new Vector2(position.x - 25, position.y), new Vector2(position.x + 25, position.y)); + + // Vertical line + Handles.DrawLine(new Vector2(position.x, position.y - 25), new Vector2(position.x, position.y + 25)); + } + + void DrawGlyph(Glyph glyph, Rect position, float scale) + { + // Get the atlas index of the glyph and lookup its atlas texture + int atlasIndex = glyph.atlasIndex; + Texture2D atlasTexture = m_fontAsset.atlasTextures.Length > atlasIndex ? m_fontAsset.atlasTextures[atlasIndex] : null; + + if (atlasTexture == null) + return; + + Material mat; + if (((GlyphRasterModes)m_fontAsset.atlasRenderMode & GlyphRasterModes.RASTER_MODE_BITMAP) == GlyphRasterModes.RASTER_MODE_BITMAP) + { + mat = internalBitmapMaterial; + + if (mat == null) + return; + + mat.mainTexture = atlasTexture; + } + else + { + mat = internalSDFMaterial; + + if (mat == null) + return; + + mat.mainTexture = atlasTexture; + mat.SetFloat(ShaderUtilities.ID_GradientScale, m_fontAsset.atlasPadding + 1); + } + + GlyphRect glyphRect = glyph.glyphRect; + + int padding = m_fontAsset.atlasPadding; + + int glyphOriginX = glyphRect.x - padding; + int glyphOriginY = glyphRect.y - padding; + int glyphWidth = glyphRect.width + padding * 2; + int glyphHeight = glyphRect.height + padding * 2; + + // Compute the normalized texture coordinates + Rect texCoords = new Rect((float)glyphOriginX / atlasTexture.width, (float)glyphOriginY / atlasTexture.height, (float)glyphWidth / atlasTexture.width, (float)glyphHeight / atlasTexture.height); + + if (Event.current.type == EventType.Repaint) + { + // Draw glyph from atlas texture. + Rect glyphDrawPosition = new Rect(position.x - padding * scale, position.y - padding * scale, position.width, position.height); + + //glyphDrawPosition.x += (glyphDrawPosition.width - glyphWidth * scale); // / 2; + //glyphDrawPosition.y += (glyphDrawPosition.height - glyphHeight * scale); // / 2; + glyphDrawPosition.width = glyphWidth * scale; + glyphDrawPosition.height = glyphHeight * scale; + + // Could switch to using the default material of the font asset which would require passing scale to the shader. + Graphics.DrawTexture(glyphDrawPosition, atlasTexture, texCoords, 0, 0, 0, 0, new Color(0.8f, 0.8f, 0.8f), mat); + } + } } } diff --git a/Scripts/Editor/TMP_FontAsset_CreationMenu.cs b/Scripts/Editor/TMP_FontAsset_CreationMenu.cs index 3116835..46cfb5f 100644 --- a/Scripts/Editor/TMP_FontAsset_CreationMenu.cs +++ b/Scripts/Editor/TMP_FontAsset_CreationMenu.cs @@ -158,6 +158,15 @@ public static void CreateFontAsset() return; } + // Make sure TMP Essential Resources have been imported in the user project. + if (TMP_Settings.instance == null) + { + Debug.Log("Unable to create font asset. Please import the TMP Essential Resources."); + + // Show Window to Import TMP Essential Resources + return; + } + for (int i = 0; i < targets.Length; i++) { Object target = targets[i]; @@ -175,7 +184,7 @@ public static void CreateFontAsset() static void CreateFontAssetFromSelectedObject(Object target) { - Font sourceFont = (Font)target; + Font font = (Font)target; string sourceFontFilePath = AssetDatabase.GetAssetPath(target); @@ -188,9 +197,9 @@ static void CreateFontAssetFromSelectedObject(Object target) FontEngine.InitializeFontEngine(); // Load Font Face - if (FontEngine.LoadFontFace(sourceFont, 90) != FontEngineError.Success) + if (FontEngine.LoadFontFace(font, 90) != FontEngineError.Success) { - Debug.LogWarning("Unable to load font face for [" + sourceFont.name + "]. Make sure \"Include Font Data\" is enabled in the Font Import Settings.", sourceFont); + Debug.LogWarning("Unable to load font face for [" + font.name + "]. Make sure \"Include Font Data\" is enabled in the Font Import Settings.", font); return; } @@ -199,13 +208,15 @@ static void CreateFontAssetFromSelectedObject(Object target) AssetDatabase.CreateAsset(fontAsset, newAssetFilePathWithName); fontAsset.version = "1.1.0"; - fontAsset.faceInfo = FontEngine.GetFaceInfo(); // Set font reference and GUID + fontAsset.sourceFontFile = font; fontAsset.m_SourceFontFileGUID = AssetDatabase.AssetPathToGUID(sourceFontFilePath); - fontAsset.m_SourceFontFile_EditorRef = sourceFont; + fontAsset.m_SourceFontFile_EditorRef = font; + fontAsset.atlasPopulationMode = AtlasPopulationMode.Dynamic; + fontAsset.clearDynamicDataOnBuild = TMP_Settings.clearDynamicDataOnBuild; // Default atlas resolution is 1024 x 1024. int atlasWidth = fontAsset.atlasWidth = 1024; @@ -250,7 +261,7 @@ static void CreateFontAssetFromSelectedObject(Object target) fontAsset.creationSettings = new FontAssetCreationSettings(fontAsset.m_SourceFontFileGUID, fontAsset.faceInfo.pointSize, 0, atlasPadding, 0, 1024, 1024, 7, string.Empty, (int)GlyphRenderMode.SDFAA); // Not sure if this is still necessary in newer versions of Unity. - EditorUtility.SetDirty(fontAsset); + //EditorUtility.SetDirty(fontAsset); AssetDatabase.SaveAssets(); } diff --git a/Scripts/Editor/TMP_GlyphPairAdjustmentRecordPropertyDrawer.cs b/Scripts/Editor/TMP_GlyphPairAdjustmentRecordPropertyDrawer.cs index 69ac6f8..801ef4f 100644 --- a/Scripts/Editor/TMP_GlyphPairAdjustmentRecordPropertyDrawer.cs +++ b/Scripts/Editor/TMP_GlyphPairAdjustmentRecordPropertyDrawer.cs @@ -10,7 +10,7 @@ namespace TMPro.EditorUtilities { [CustomPropertyDrawer(typeof(TMP_GlyphPairAdjustmentRecord))] - public class TMP_GlyphPairAdjustmentRecordPropertyDrawer : PropertyDrawer + internal class TMP_GlyphPairAdjustmentRecordPropertyDrawer : PropertyDrawer { private bool isEditingEnabled = false; private bool isSelectable = false; diff --git a/Scripts/Editor/TMP_InputFieldEditor.cs b/Scripts/Editor/TMP_InputFieldEditor.cs index 740379e..a430025 100644 --- a/Scripts/Editor/TMP_InputFieldEditor.cs +++ b/Scripts/Editor/TMP_InputFieldEditor.cs @@ -50,7 +50,9 @@ private struct m_foldout SerializedProperty m_RichText; SerializedProperty m_RichTextEditingAllowed; SerializedProperty m_ResetOnDeActivation; + SerializedProperty m_KeepTextSelectionVisible; SerializedProperty m_RestoreOriginalTextOnEscape; + SerializedProperty m_ShouldActivateOnSelect; SerializedProperty m_OnFocusSelectAll; SerializedProperty m_GlobalPointSize; @@ -97,9 +99,12 @@ protected override void OnEnable() m_RichText = serializedObject.FindProperty("m_RichText"); m_RichTextEditingAllowed = serializedObject.FindProperty("m_isRichTextEditingAllowed"); m_ResetOnDeActivation = serializedObject.FindProperty("m_ResetOnDeActivation"); + m_KeepTextSelectionVisible = serializedObject.FindProperty("m_KeepTextSelectionVisible"); m_RestoreOriginalTextOnEscape = serializedObject.FindProperty("m_RestoreOriginalTextOnEscape"); m_OnFocusSelectAll = serializedObject.FindProperty("m_OnFocusSelectAll"); + m_ShouldActivateOnSelect = serializedObject.FindProperty("m_ShouldActivateOnSelect"); + m_GlobalPointSize = serializedObject.FindProperty("m_GlobalPointSize"); m_GlobalFontAsset = serializedObject.FindProperty("m_GlobalFontAsset"); @@ -186,10 +191,10 @@ public override void OnInspectorGUI() if (text != null) { if (m_LineType.enumValueIndex == (int)TMP_InputField.LineType.SingleLine) - text.enableWordWrapping = false; + text.textWrappingMode = TextWrappingModes.PreserveWhitespaceNoWrap; else { - text.enableWordWrapping = true; + text.textWrappingMode = TextWrappingModes.Normal; } } } @@ -254,9 +259,17 @@ public override void OnInspectorGUI() { EditorGUI.indentLevel++; - EditorGUILayout.PropertyField(m_OnFocusSelectAll, new GUIContent("OnFocus - Select All", "Should all the text be selected when the Input Field is selected.")); - EditorGUILayout.PropertyField(m_ResetOnDeActivation, new GUIContent("Reset On DeActivation", "Should the Text and Caret position be reset when Input Field is DeActivated.")); - EditorGUILayout.PropertyField(m_RestoreOriginalTextOnEscape, new GUIContent("Restore On ESC Key", "Should the original text be restored when pressing ESC.")); + EditorGUILayout.PropertyField(m_OnFocusSelectAll, new GUIContent("OnFocus - Select All", "Should all the text be selected when the Input Field is selected?")); + EditorGUILayout.PropertyField(m_ResetOnDeActivation, new GUIContent("Reset On Deactivation", "Should the Text and Caret position be reset when Input Field looses focus and is Deactivated?")); + + EditorGUI.indentLevel++; + GUI.enabled = !m_ResetOnDeActivation.boolValue; + EditorGUILayout.PropertyField(m_KeepTextSelectionVisible, new GUIContent("Keep Text Selection Visible", "Should the text selection remain visible when the input field looses focus and is deactivated?")); + GUI.enabled = true; + EditorGUI.indentLevel--; + + EditorGUILayout.PropertyField(m_RestoreOriginalTextOnEscape, new GUIContent("Restore On ESC Key", "Should the original text be restored when pressing ESC?")); + EditorGUILayout.PropertyField(m_ShouldActivateOnSelect, new GUIContent("Should Activate On Select", "Determines if the Input Field will be activated when selected.")); EditorGUILayout.PropertyField(m_HideMobileKeyboard, new GUIContent("Hide Soft Keyboard", "Controls the visibility of the mobile virtual keyboard.")); EditorGUILayout.PropertyField(m_HideMobileInput, new GUIContent("Hide Mobile Input", "Controls the visibility of the editable text field above the mobile virtual keyboard.")); EditorGUILayout.PropertyField(m_ReadOnly); diff --git a/Scripts/Editor/TMP_MarkToBaseAdjustmentRecordPropertyDrawer.cs b/Scripts/Editor/TMP_MarkToBaseAdjustmentRecordPropertyDrawer.cs new file mode 100644 index 0000000..ef3b8ff --- /dev/null +++ b/Scripts/Editor/TMP_MarkToBaseAdjustmentRecordPropertyDrawer.cs @@ -0,0 +1,271 @@ +using UnityEngine; +using UnityEngine.TextCore; +using UnityEngine.TextCore.LowLevel; +using UnityEditor; +using System.Collections; +using System.Text.RegularExpressions; + + +namespace TMPro.EditorUtilities +{ + + [CustomPropertyDrawer(typeof(MarkToBaseAdjustmentRecord))] + public class TMP_MarkToBaseAdjustmentRecordPropertyDrawer : PropertyDrawer + { + private bool isEditingEnabled = false; + private bool isSelectable = false; + + private string m_PreviousInput; + + private TMP_FontAsset m_FontAsset; + + static GUIContent s_CharacterTextFieldLabel = new GUIContent("Char:", "Enter the character or its UTF16 or UTF32 Unicode character escape sequence. For UTF16 use \"\\uFF00\" and for UTF32 use \"\\UFF00FF00\" representation."); + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + SerializedProperty prop_BaseGlyphID = property.FindPropertyRelative("m_BaseGlyphID"); + SerializedProperty prop_BaseGlyphAnchorPoint = property.FindPropertyRelative("m_BaseGlyphAnchorPoint"); + + SerializedProperty prop_MarkGlyphID = property.FindPropertyRelative("m_MarkGlyphID"); + SerializedProperty prop_MarkAdjustmentRecord = property.FindPropertyRelative("m_MarkPositionAdjustment"); + + position.yMin += 2; + + float width = position.width / 2; + float padding = 5.0f; + + Rect rect; + + isEditingEnabled = GUI.enabled; + isSelectable = label.text == "Selectable" ? true : false; + + if (isSelectable) + GUILayoutUtility.GetRect(position.width, 75); + else + GUILayoutUtility.GetRect(position.width, 55); + + GUIStyle style = new GUIStyle(EditorStyles.label) {richText = true}; + + // Base Glyph + GUI.enabled = isEditingEnabled; + if (isSelectable) + { + float labelWidth = GUI.skin.label.CalcSize(new GUIContent("ID: " + prop_BaseGlyphID.intValue)).x; + + if (!isEditingEnabled) + { + EditorGUI.LabelField(new Rect(position.x + (64 - labelWidth) / 2, position.y + 60, 64f, 18f), new GUIContent("ID: " + prop_BaseGlyphID.intValue + ""), style); + } + else + { + EditorGUI.BeginChangeCheck(); + EditorGUIUtility.labelWidth = 25f; + EditorGUI.DelayedIntField(new Rect(position.x + (64 - labelWidth) / 2, position.y + 60, 64, 18), prop_BaseGlyphID, new GUIContent("ID:")); + if (EditorGUI.EndChangeCheck()) + { + TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset; + if (fontAsset != null) + { + property.serializedObject.ApplyModifiedProperties(); + fontAsset.ReadFontAssetDefinition(); + TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset); + } + } + } + + GUI.enabled = isEditingEnabled; + EditorGUIUtility.labelWidth = 30f; + + rect = new Rect(position.x + 70, position.y + 10, (width - 70) - padding, 18); + EditorGUI.PropertyField(rect, prop_BaseGlyphAnchorPoint.FindPropertyRelative("m_XCoordinate"), new GUIContent("X:")); + + rect.y += 20; + EditorGUI.PropertyField(rect, prop_BaseGlyphAnchorPoint.FindPropertyRelative("m_YCoordinate"), new GUIContent("Y:")); + + DrawGlyph((uint)prop_BaseGlyphID.intValue, new Rect(position.x, position.y, position.width, position.height), property); + } + + // Mark Glyph + GUI.enabled = isEditingEnabled; + if (isSelectable) + { + float labelWidth = GUI.skin.label.CalcSize(new GUIContent("ID: " + prop_MarkGlyphID.intValue)).x; + + if (!isEditingEnabled) + { + EditorGUI.LabelField(new Rect(position.width / 2 + 20 + (64 - labelWidth) / 2, position.y + 60, 64f, 18f), new GUIContent("ID: " + prop_MarkGlyphID.intValue + ""), style); + } + else + { + EditorGUI.BeginChangeCheck(); + EditorGUIUtility.labelWidth = 25f; + EditorGUI.DelayedIntField(new Rect(position.width / 2 + 20 + (64 - labelWidth) / 2, position.y + 60, 64, 18), prop_MarkGlyphID, new GUIContent("ID:")); + if (EditorGUI.EndChangeCheck()) + { + TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset; + if (fontAsset != null) + { + property.serializedObject.ApplyModifiedProperties(); + fontAsset.ReadFontAssetDefinition(); + TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset); + } + } + } + + GUI.enabled = isEditingEnabled; + EditorGUIUtility.labelWidth = 30f; + + rect = new Rect(position.width / 2 + 20 + 70, position.y + 10, (width - 70) - padding, 18); + EditorGUI.PropertyField(rect, prop_MarkAdjustmentRecord.FindPropertyRelative("m_XPositionAdjustment"), new GUIContent("X:")); + + rect.y += 20; + EditorGUI.PropertyField(rect, prop_MarkAdjustmentRecord.FindPropertyRelative("m_YPositionAdjustment"), new GUIContent("Y:")); + + DrawGlyph((uint)prop_MarkGlyphID.intValue, new Rect(position.width / 2 + 20, position.y, position.width, position.height), property); + } + } + + bool ValidateInput(string source) + { + int length = string.IsNullOrEmpty(source) ? 0 : source.Length; + + ////Filter out unwanted characters. + Event evt = Event.current; + + char c = evt.character; + + if (c != '\0') + { + switch (length) + { + case 0: + break; + case 1: + if (source != m_PreviousInput) + return true; + + if ((source[0] == '\\' && (c == 'u' || c == 'U')) == false) + evt.character = '\0'; + + break; + case 2: + case 3: + case 4: + case 5: + if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) + evt.character = '\0'; + break; + case 6: + case 7: + case 8: + case 9: + if (source[1] == 'u' || (c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) + evt.character = '\0'; + + // Validate input + if (length == 6 && source[1] == 'u' && source != m_PreviousInput) + return true; + break; + case 10: + if (source != m_PreviousInput) + return true; + + evt.character = '\0'; + break; + } + } + + m_PreviousInput = source; + + return false; + } + + uint GetUnicodeCharacter (string source) + { + uint unicode; + + if (source.Length == 1) + unicode = source[0]; + else if (source.Length == 6) + unicode = (uint)TMP_TextUtilities.StringHexToInt(source.Replace("\\u", "")); + else + unicode = (uint)TMP_TextUtilities.StringHexToInt(source.Replace("\\U", "")); + + return unicode; + } + + void DrawGlyph(uint glyphIndex, Rect position, SerializedProperty property) + { + // Get a reference to the font asset + TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset; + + if (fontAsset == null) + return; + + Glyph glyph; + + // Check if glyph is present in the atlas texture. + if (!fontAsset.glyphLookupTable.TryGetValue(glyphIndex, out glyph)) + return; + + // Get the atlas index of the glyph and lookup its atlas texture + int atlasIndex = glyph.atlasIndex; + Texture2D atlasTexture = fontAsset.atlasTextures.Length > atlasIndex ? fontAsset.atlasTextures[atlasIndex] : null; + + if (atlasTexture == null) + return; + + Material mat; + if (((GlyphRasterModes)fontAsset.atlasRenderMode & GlyphRasterModes.RASTER_MODE_BITMAP) == GlyphRasterModes.RASTER_MODE_BITMAP) + { + mat = TMP_FontAssetEditor.internalBitmapMaterial; + + if (mat == null) + return; + + mat.mainTexture = atlasTexture; + } + else + { + mat = TMP_FontAssetEditor.internalSDFMaterial; + + if (mat == null) + return; + + mat.mainTexture = atlasTexture; + mat.SetFloat(ShaderUtilities.ID_GradientScale, fontAsset.atlasPadding + 1); + } + + // Draw glyph from atlas texture. + Rect glyphDrawPosition = new Rect(position.x, position.y + 2, 64, 60); + + GlyphRect glyphRect = glyph.glyphRect; + + int padding = fontAsset.atlasPadding; + + int glyphOriginX = glyphRect.x - padding; + int glyphOriginY = glyphRect.y - padding; + int glyphWidth = glyphRect.width + padding * 2; + int glyphHeight = glyphRect.height + padding * 2; + + float normalizedHeight = fontAsset.faceInfo.ascentLine - fontAsset.faceInfo.descentLine; + float scale = glyphDrawPosition.width / normalizedHeight; + + // Compute the normalized texture coordinates + Rect texCoords = new Rect((float)glyphOriginX / atlasTexture.width, (float)glyphOriginY / atlasTexture.height, (float)glyphWidth / atlasTexture.width, (float)glyphHeight / atlasTexture.height); + + if (Event.current.type == EventType.Repaint) + { + glyphDrawPosition.x += (glyphDrawPosition.width - glyphWidth * scale) / 2; + glyphDrawPosition.y += (glyphDrawPosition.height - glyphHeight * scale) / 2; + glyphDrawPosition.width = glyphWidth * scale; + glyphDrawPosition.height = glyphHeight * scale; + + // Could switch to using the default material of the font asset which would require passing scale to the shader. + Graphics.DrawTexture(glyphDrawPosition, atlasTexture, texCoords, 0, 0, 0, 0, new Color(1f, 1f, 1f), mat); + } + } + + + } +} diff --git a/Scripts/Editor/TMP_MarkToBaseAdjustmentRecordPropertyDrawer.cs.meta b/Scripts/Editor/TMP_MarkToBaseAdjustmentRecordPropertyDrawer.cs.meta new file mode 100644 index 0000000..8f5655a --- /dev/null +++ b/Scripts/Editor/TMP_MarkToBaseAdjustmentRecordPropertyDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 065a7fcf0d7286d418f3114ec18d1a2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/TMP_MarkToMarkAdjustmentRecordPropertyDrawer.cs b/Scripts/Editor/TMP_MarkToMarkAdjustmentRecordPropertyDrawer.cs new file mode 100644 index 0000000..ad36793 --- /dev/null +++ b/Scripts/Editor/TMP_MarkToMarkAdjustmentRecordPropertyDrawer.cs @@ -0,0 +1,270 @@ +using UnityEngine; +using UnityEngine.TextCore; +using UnityEngine.TextCore.LowLevel; +using UnityEditor; +using System.Collections; +using System.Text.RegularExpressions; + + +namespace TMPro.EditorUtilities +{ + [CustomPropertyDrawer(typeof(MarkToMarkAdjustmentRecord))] + public class TMP_MarkToMarkAdjustmentRecordPropertyDrawer : PropertyDrawer + { + private bool isEditingEnabled = false; + private bool isSelectable = false; + + private string m_PreviousInput; + + private TMP_FontAsset m_FontAsset; + + static GUIContent s_CharacterTextFieldLabel = new GUIContent("Char:", "Enter the character or its UTF16 or UTF32 Unicode character escape sequence. For UTF16 use \"\\uFF00\" and for UTF32 use \"\\UFF00FF00\" representation."); + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + SerializedProperty prop_BaseGlyphID = property.FindPropertyRelative("m_BaseMarkGlyphID"); + SerializedProperty prop_BaseGlyphAnchorPoint = property.FindPropertyRelative("m_BaseMarkGlyphAnchorPoint"); + + SerializedProperty prop_MarkGlyphID = property.FindPropertyRelative("m_CombiningMarkGlyphID"); + SerializedProperty prop_MarkAdjustmentRecord = property.FindPropertyRelative("m_CombiningMarkPositionAdjustment"); + + position.yMin += 2; + + float width = position.width / 2; + float padding = 5.0f; + + Rect rect; + + isEditingEnabled = GUI.enabled; + isSelectable = label.text == "Selectable" ? true : false; + + if (isSelectable) + GUILayoutUtility.GetRect(position.width, 75); + else + GUILayoutUtility.GetRect(position.width, 55); + + GUIStyle style = new GUIStyle(EditorStyles.label) {richText = true}; + + // Base Glyph + GUI.enabled = isEditingEnabled; + if (isSelectable) + { + float labelWidth = GUI.skin.label.CalcSize(new GUIContent("ID: " + prop_BaseGlyphID.intValue)).x; + + if (!isEditingEnabled) + { + EditorGUI.LabelField(new Rect(position.x + (64 - labelWidth) / 2, position.y + 60, 64f, 18f), new GUIContent("ID: " + prop_BaseGlyphID.intValue + ""), style); + } + else + { + EditorGUI.BeginChangeCheck(); + EditorGUIUtility.labelWidth = 25f; + EditorGUI.DelayedIntField(new Rect(position.x + (64 - labelWidth) / 2, position.y + 60, 64, 18), prop_BaseGlyphID, new GUIContent("ID:")); + if (EditorGUI.EndChangeCheck()) + { + TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset; + if (fontAsset != null) + { + property.serializedObject.ApplyModifiedProperties(); + fontAsset.ReadFontAssetDefinition(); + TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset); + } + } + } + + GUI.enabled = isEditingEnabled; + EditorGUIUtility.labelWidth = 30f; + + rect = new Rect(position.x + 70, position.y + 10, (width - 70) - padding, 18); + EditorGUI.PropertyField(rect, prop_BaseGlyphAnchorPoint.FindPropertyRelative("m_XCoordinate"), new GUIContent("X:")); + + rect.y += 20; + EditorGUI.PropertyField(rect, prop_BaseGlyphAnchorPoint.FindPropertyRelative("m_YCoordinate"), new GUIContent("Y:")); + + DrawGlyph((uint)prop_BaseGlyphID.intValue, new Rect(position.x, position.y, position.width, position.height), property); + } + + // Mark Glyph + GUI.enabled = isEditingEnabled; + if (isSelectable) + { + float labelWidth = GUI.skin.label.CalcSize(new GUIContent("ID: " + prop_MarkGlyphID.intValue)).x; + + if (!isEditingEnabled) + { + EditorGUI.LabelField(new Rect(position.width / 2 + 20 + (64 - labelWidth) / 2, position.y + 60, 64f, 18f), new GUIContent("ID: " + prop_MarkGlyphID.intValue + ""), style); + } + else + { + EditorGUI.BeginChangeCheck(); + EditorGUIUtility.labelWidth = 25f; + EditorGUI.DelayedIntField(new Rect(position.width / 2 + 20 + (64 - labelWidth) / 2, position.y + 60, 64, 18), prop_MarkGlyphID, new GUIContent("ID:")); + if (EditorGUI.EndChangeCheck()) + { + TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset; + if (fontAsset != null) + { + property.serializedObject.ApplyModifiedProperties(); + fontAsset.ReadFontAssetDefinition(); + TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset); + } + } + } + + GUI.enabled = isEditingEnabled; + EditorGUIUtility.labelWidth = 30f; + + rect = new Rect(position.width / 2 + 20 + 70, position.y + 10, (width - 70) - padding, 18); + EditorGUI.PropertyField(rect, prop_MarkAdjustmentRecord.FindPropertyRelative("m_XPositionAdjustment"), new GUIContent("X:")); + + rect.y += 20; + EditorGUI.PropertyField(rect, prop_MarkAdjustmentRecord.FindPropertyRelative("m_YPositionAdjustment"), new GUIContent("Y:")); + + DrawGlyph((uint)prop_MarkGlyphID.intValue, new Rect(position.width / 2 + 20, position.y, position.width, position.height), property); + } + } + + bool ValidateInput(string source) + { + int length = string.IsNullOrEmpty(source) ? 0 : source.Length; + + ////Filter out unwanted characters. + Event evt = Event.current; + + char c = evt.character; + + if (c != '\0') + { + switch (length) + { + case 0: + break; + case 1: + if (source != m_PreviousInput) + return true; + + if ((source[0] == '\\' && (c == 'u' || c == 'U')) == false) + evt.character = '\0'; + + break; + case 2: + case 3: + case 4: + case 5: + if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) + evt.character = '\0'; + break; + case 6: + case 7: + case 8: + case 9: + if (source[1] == 'u' || (c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) + evt.character = '\0'; + + // Validate input + if (length == 6 && source[1] == 'u' && source != m_PreviousInput) + return true; + break; + case 10: + if (source != m_PreviousInput) + return true; + + evt.character = '\0'; + break; + } + } + + m_PreviousInput = source; + + return false; + } + + uint GetUnicodeCharacter (string source) + { + uint unicode; + + if (source.Length == 1) + unicode = source[0]; + else if (source.Length == 6) + unicode = (uint)TMP_TextUtilities.StringHexToInt(source.Replace("\\u", "")); + else + unicode = (uint)TMP_TextUtilities.StringHexToInt(source.Replace("\\U", "")); + + return unicode; + } + + void DrawGlyph(uint glyphIndex, Rect position, SerializedProperty property) + { + // Get a reference to the font asset + TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset; + + if (fontAsset == null) + return; + + Glyph glyph; + + // Check if glyph is present in the atlas texture. + if (!fontAsset.glyphLookupTable.TryGetValue(glyphIndex, out glyph)) + return; + + // Get the atlas index of the glyph and lookup its atlas texture + int atlasIndex = glyph.atlasIndex; + Texture2D atlasTexture = fontAsset.atlasTextures.Length > atlasIndex ? fontAsset.atlasTextures[atlasIndex] : null; + + if (atlasTexture == null) + return; + + Material mat; + if (((GlyphRasterModes)fontAsset.atlasRenderMode & GlyphRasterModes.RASTER_MODE_BITMAP) == GlyphRasterModes.RASTER_MODE_BITMAP) + { + mat = TMP_FontAssetEditor.internalBitmapMaterial; + + if (mat == null) + return; + + mat.mainTexture = atlasTexture; + } + else + { + mat = TMP_FontAssetEditor.internalSDFMaterial; + + if (mat == null) + return; + + mat.mainTexture = atlasTexture; + mat.SetFloat(ShaderUtilities.ID_GradientScale, fontAsset.atlasPadding + 1); + } + + // Draw glyph from atlas texture. + Rect glyphDrawPosition = new Rect(position.x, position.y + 2, 64, 60); + + GlyphRect glyphRect = glyph.glyphRect; + + int padding = fontAsset.atlasPadding; + + int glyphOriginX = glyphRect.x - padding; + int glyphOriginY = glyphRect.y - padding; + int glyphWidth = glyphRect.width + padding * 2; + int glyphHeight = glyphRect.height + padding * 2; + + float normalizedHeight = fontAsset.faceInfo.ascentLine - fontAsset.faceInfo.descentLine; + float scale = glyphDrawPosition.width / normalizedHeight; + + // Compute the normalized texture coordinates + Rect texCoords = new Rect((float)glyphOriginX / atlasTexture.width, (float)glyphOriginY / atlasTexture.height, (float)glyphWidth / atlasTexture.width, (float)glyphHeight / atlasTexture.height); + + if (Event.current.type == EventType.Repaint) + { + glyphDrawPosition.x += (glyphDrawPosition.width - glyphWidth * scale) / 2; + glyphDrawPosition.y += (glyphDrawPosition.height - glyphHeight * scale) / 2; + glyphDrawPosition.width = glyphWidth * scale; + glyphDrawPosition.height = glyphHeight * scale; + + // Could switch to using the default material of the font asset which would require passing scale to the shader. + Graphics.DrawTexture(glyphDrawPosition, atlasTexture, texCoords, 0, 0, 0, 0, new Color(1f, 1f, 1f), mat); + } + } + + + } +} diff --git a/Scripts/Editor/TMP_MarkToMarkAdjustmentRecordPropertyDrawer.cs.meta b/Scripts/Editor/TMP_MarkToMarkAdjustmentRecordPropertyDrawer.cs.meta new file mode 100644 index 0000000..8e3062e --- /dev/null +++ b/Scripts/Editor/TMP_MarkToMarkAdjustmentRecordPropertyDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 149e85704eb73624ba2d705408274f3e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/TMP_MarkupTagUpdateUtility.cs b/Scripts/Editor/TMP_MarkupTagUpdateUtility.cs new file mode 100644 index 0000000..ffbff32 --- /dev/null +++ b/Scripts/Editor/TMP_MarkupTagUpdateUtility.cs @@ -0,0 +1,282 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + + +internal class TMP_MarkupTagUpdateUtility +{ + struct MarkupTagDescriptor + { + public string name; + public string tag; + public string description; + + public MarkupTagDescriptor(string name, string tag, string description) + { + this.name = name; + this.tag = tag; + this.description = description; + } + + public MarkupTagDescriptor(string name) + { + this.name = name; + this.tag = null; + this.description = null; + } + + public static MarkupTagDescriptor linefeed = new MarkupTagDescriptor("\n"); + } + + private static MarkupTagDescriptor[] m_MarkupTags = + { + new MarkupTagDescriptor("BOLD", "b", "// "), + new MarkupTagDescriptor("SLASH_BOLD", "/b", "// "), + new MarkupTagDescriptor("ITALIC", "i", "// "), + new MarkupTagDescriptor("SLASH_ITALIC", "/i", "// "), + new MarkupTagDescriptor("UNDERLINE", "u", "// "), + new MarkupTagDescriptor("SLASH_UNDERLINE", "/u", "// "), + new MarkupTagDescriptor("STRIKETHROUGH", "s", "// "), + new MarkupTagDescriptor("SLASH_STRIKETHROUGH", "/s", "// "), + new MarkupTagDescriptor("SUBSCRIPT", "sub", "// "), + new MarkupTagDescriptor("SLASH_SUBSCRIPT", "/sub", "// "), + new MarkupTagDescriptor("SUPERSCRIPT", "sup", "// "), + new MarkupTagDescriptor("SLASH_SUPERSCRIPT", "/sup", "// "), + new MarkupTagDescriptor("MARK", "mark", "// "), + new MarkupTagDescriptor("SLASH_MARK", "/mark", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("COLOR", "color", "// "), + new MarkupTagDescriptor("SLASH_COLOR", "/color", "// "), + new MarkupTagDescriptor("ALPHA", "alpha", "// "), + new MarkupTagDescriptor("SLASH_ALPHA", "/alpha", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("FONT", "font", "// or " ), + new MarkupTagDescriptor("SLASH_FONT", "/font", "// "), + new MarkupTagDescriptor("MATERIAL", "material", "// or as attribute "), + new MarkupTagDescriptor("SLASH_MATERIAL", "/material", "// "), + new MarkupTagDescriptor("SIZE", "size", "// "), + new MarkupTagDescriptor("SLASH_SIZE", "/size", "// "), + new MarkupTagDescriptor("FONT_WEIGHT", "font-weight", "// "), + new MarkupTagDescriptor("SLASH_FONT_WEIGHT", "/font-weight", "// "), + new MarkupTagDescriptor("SCALE", "scale", "// "), + new MarkupTagDescriptor("SLASH_SCALE", "/scale", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("SPRITE", "sprite", "// "), + new MarkupTagDescriptor("STYLE", "style", "// "), + new MarkupTagDescriptor("GRADIENT", "gradient", "// "), + new MarkupTagDescriptor("SLASH_GRADIENT", "/gradient", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("A", "a", "// "), + new MarkupTagDescriptor("SLASH_A", "/a", "// "), + new MarkupTagDescriptor("LINK", "link", "// "), + new MarkupTagDescriptor("SLASH_LINK", "/link", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("POSITION", "pos", "// "), + new MarkupTagDescriptor("SLASH_POSITION", "/pos", "// "), + new MarkupTagDescriptor("VERTICAL_OFFSET", "voffset","// "), + new MarkupTagDescriptor("SLASH_VERTICAL_OFFSET", "/voffset", "// "), + new MarkupTagDescriptor("ROTATE", "rotate", "// "), + new MarkupTagDescriptor("SLASH_ROTATE", "/rotate", "// "), + new MarkupTagDescriptor("TRANSFORM", "transform","// "), + new MarkupTagDescriptor("SLASH_TRANSFORM", "/transform", "// "), + new MarkupTagDescriptor("SPACE", "space", "// "), + new MarkupTagDescriptor("SLASH_SPACE", "/space", "// "), + new MarkupTagDescriptor("CHARACTER_SPACE", "cspace", "// "), + new MarkupTagDescriptor("SLASH_CHARACTER_SPACE", "/cspace", "// "), + new MarkupTagDescriptor("MONOSPACE", "mspace", "// "), + new MarkupTagDescriptor("SLASH_MONOSPACE", "/mspace", "// "), + new MarkupTagDescriptor("CHARACTER_SPACING", "character-spacing", "// "), + new MarkupTagDescriptor("SLASH_CHARACTER_SPACING", "/character-spacing", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("ALIGN", "align", "// "), + new MarkupTagDescriptor("SLASH_ALIGN", "/align", "// "), + new MarkupTagDescriptor("WIDTH", "width", "// "), + new MarkupTagDescriptor("SLASH_WIDTH", "/width", "// "), + new MarkupTagDescriptor("MARGIN", "margin", "// "), + new MarkupTagDescriptor("SLASH_MARGIN", "/margin", "// "), + new MarkupTagDescriptor("MARGIN_LEFT", "margin-left", "// "), + new MarkupTagDescriptor("MARGIN_RIGHT", "margin-right", "// "), + new MarkupTagDescriptor("INDENT", "indent", "// "), + new MarkupTagDescriptor("SLASH_INDENT", "/indent", "// "), + new MarkupTagDescriptor("LINE_INDENT", "line-indent", "// "), + new MarkupTagDescriptor("SLASH_LINE_INDENT", "/line-indent", "// "), + new MarkupTagDescriptor("LINE_HEIGHT", "line-height", "// "), + new MarkupTagDescriptor("SLASH_LINE_HEIGHT", "/line-height", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("NO_BREAK", "nobr", "// "), + new MarkupTagDescriptor("SLASH_NO_BREAK", "/nobr", "// "), + new MarkupTagDescriptor("NO_PARSE", "noparse","// "), + new MarkupTagDescriptor("SLASH_NO_PARSE", "/noparse", "// "), + new MarkupTagDescriptor("PAGE", "page", "// "), + new MarkupTagDescriptor("SLASH_PAGE", "/page", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("ACTION", "action", "// "), + new MarkupTagDescriptor("SLASH_ACTION", "/action", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("CLASS", "class", "// "), + new MarkupTagDescriptor("TABLE", "table", "// "), + new MarkupTagDescriptor("SLASH_TABLE", "/table", "//
"), + new MarkupTagDescriptor("TH", "th", "// "), + new MarkupTagDescriptor("SLASH_TH", "/th", "// "), + new MarkupTagDescriptor("TR", "tr", "// "), + new MarkupTagDescriptor("SLASH_TR", "/tr", "// "), + new MarkupTagDescriptor("TD", "td", "// "), + new MarkupTagDescriptor("SLASH_TD", "/td", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("// Text Styles"), + new MarkupTagDescriptor("LOWERCASE", "lowercase", "// "), + new MarkupTagDescriptor("SLASH_LOWERCASE", "/lowercase", "// "), + new MarkupTagDescriptor("ALLCAPS", "allcaps", "// "), + new MarkupTagDescriptor("SLASH_ALLCAPS", "/allcaps", "// "), + new MarkupTagDescriptor("UPPERCASE", "uppercase", "// "), + new MarkupTagDescriptor("SLASH_UPPERCASE", "/uppercase", "// "), + new MarkupTagDescriptor("SMALLCAPS", "smallcaps", "// "), + new MarkupTagDescriptor("SLASH_SMALLCAPS", "/smallcaps", "// "), + new MarkupTagDescriptor("CAPITALIZE", "capitalize", "// "), + new MarkupTagDescriptor("SLASH_CAPITALIZE", "/capitalize", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("// Font Features"), + new MarkupTagDescriptor("LIGA", "liga", "// "), + new MarkupTagDescriptor("SLASH_LIGA", "/liga", "// "), + new MarkupTagDescriptor("FRAC", "frac", "// "), + new MarkupTagDescriptor("SLASH_FRAC", "/frac", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("// Attributes"), + new MarkupTagDescriptor("NAME", "name", "// "), + new MarkupTagDescriptor("INDEX", "index", "// "), + new MarkupTagDescriptor("TINT", "tint", "// "), + new MarkupTagDescriptor("ANIM", "anim", "// "), + new MarkupTagDescriptor("HREF", "href", "// text to be displayed."), + new MarkupTagDescriptor("ANGLE", "angle", "// Italic Slant Angle"), + new MarkupTagDescriptor("FAMILY", "family", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("// Named Colors"), + new MarkupTagDescriptor("RED", "red",""), + new MarkupTagDescriptor("GREEN", "green", ""), + new MarkupTagDescriptor("BLUE", "blue", ""), + new MarkupTagDescriptor("WHITE", "white", ""), + new MarkupTagDescriptor("BLACK", "black", ""), + new MarkupTagDescriptor("CYAN", "cyna", ""), + new MarkupTagDescriptor("MAGENTA", "magenta", ""), + new MarkupTagDescriptor("YELLOW", "yellow", ""), + new MarkupTagDescriptor("ORANGE", "orange", ""), + new MarkupTagDescriptor("PURPLE", "purple", ""), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("// Unicode Characters"), + new MarkupTagDescriptor("BR", "br", "//
Line Feed (LF) \\u0A"), + new MarkupTagDescriptor("ZWSP", "zwsp", "// Zero Width Space \\u200B"), + new MarkupTagDescriptor("NBSP", "nbsp", "// Non Breaking Space \\u00A0"), + new MarkupTagDescriptor("SHY", "shy", "// Soft Hyphen \\u00AD"), + new MarkupTagDescriptor("ZWJ", "zwj", "// Zero Width Joiner \\u200D"), + new MarkupTagDescriptor("WJ", "wj", "// Word Joiner \\u2060"), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("// Alignment"), + new MarkupTagDescriptor("LEFT", "left", "// "), + new MarkupTagDescriptor("RIGHT", "right", "// "), + new MarkupTagDescriptor("CENTER", "center", "// "), + new MarkupTagDescriptor("JUSTIFIED", "justified", "// "), + new MarkupTagDescriptor("FLUSH", "flush", "// "), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("// Prefix and Unit suffix"), + new MarkupTagDescriptor("NONE", "none", ""), + new MarkupTagDescriptor("PLUS", "+", ""), + new MarkupTagDescriptor("MINUS", "-", ""), + new MarkupTagDescriptor("PX", "px", ""), + new MarkupTagDescriptor("PLUS_PX", "+px", ""), + new MarkupTagDescriptor("MINUS_PX", "-px", ""), + new MarkupTagDescriptor("EM", "em", ""), + new MarkupTagDescriptor("PLUS_EM", "+em", ""), + new MarkupTagDescriptor("MINUS_EM", "-em", ""), + new MarkupTagDescriptor("PCT", "pct", ""), + new MarkupTagDescriptor("PLUS_PCT", "+pct", ""), + new MarkupTagDescriptor("MINUS_PCT", "-pct", ""), + new MarkupTagDescriptor("PERCENTAGE", "%", ""), + new MarkupTagDescriptor("PLUS_PERCENTAGE", "+%", ""), + new MarkupTagDescriptor("MINUS_PERCENTAGE", "-%", ""), + new MarkupTagDescriptor("HASH", "#", "// #"), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("TRUE", "true", ""), + new MarkupTagDescriptor("FALSE", "false", ""), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("INVALID", "invalid", ""), + MarkupTagDescriptor.linefeed, + + new MarkupTagDescriptor("NORMAL", "normal", "// "), + new MarkupTagDescriptor("DEFAULT", "default", "// "), + }; + + + [MenuItem("Window/TextMeshPro/Internal/Update Markup Tag Hash Codes", false, 2200, true)] + static void UpdateMarkupTagHashCodes() + { + Dictionary markupHashCodes = new Dictionary(); + string output = string.Empty; + + for (int i = 0; i < m_MarkupTags.Length; i++) + { + MarkupTagDescriptor descriptor = m_MarkupTags[i]; + int hashCode = descriptor.tag == null ? 0 : GetHashCodeCaseInSensitive(descriptor.tag); + + if (descriptor.name == "\n") + output += "\n"; + else if (hashCode == 0) + output += descriptor.name + "\n"; + else + { + output += descriptor.name + " = " + hashCode + ",\t" + descriptor.description + "\n"; + + if (markupHashCodes.ContainsKey(hashCode) == false) + markupHashCodes.Add(hashCode, descriptor); + else + Debug.Log("[" + descriptor.name + "] with HashCode [" + hashCode + "] collides with [" + markupHashCodes[hashCode].name + "]."); + } + } + + Debug.Log(output); + } + + /// + /// Table used to convert character to uppercase. + /// + const string k_lookupStringU = "-------------------------------- !-#$%&-()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[-]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~-"; + + /// + /// Get uppercase version of this ASCII character. + /// + public static char ToUpperFast(char c) + { + if (c > k_lookupStringU.Length - 1) + return c; + + return k_lookupStringU[c]; + } + + public static int GetHashCodeCaseInSensitive(string s) + { + int hashCode = 5381; + + for (int i = 0; i < s.Length; i++) + hashCode = (hashCode << 5) + hashCode ^ ToUpperFast(s[i]); + + return hashCode; + } +} diff --git a/Scripts/Editor/TMP_MarkupTagUpdateUtility.cs.meta b/Scripts/Editor/TMP_MarkupTagUpdateUtility.cs.meta new file mode 100644 index 0000000..c7b8ae8 --- /dev/null +++ b/Scripts/Editor/TMP_MarkupTagUpdateUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 225b67dcce9247b4c806e435b34695d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/TMP_PostBuildProcessHandler.cs b/Scripts/Editor/TMP_PostBuildProcessHandler.cs index d27a1a0..c3546e1 100644 --- a/Scripts/Editor/TMP_PostBuildProcessHandler.cs +++ b/Scripts/Editor/TMP_PostBuildProcessHandler.cs @@ -16,7 +16,7 @@ public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProj // Try loading the TMP Settings TMP_Settings settings = Resources.Load("TMP Settings"); - if (settings == null) + if (settings == null || TMP_Settings.enableEmojiSupport == false) return; string file = Path.Combine(pathToBuiltProject, "Classes/UI/Keyboard.mm"); diff --git a/Scripts/Editor/TMP_PreBuildProcessor.cs b/Scripts/Editor/TMP_PreBuildProcessor.cs index bf9b02b..c4addd2 100644 --- a/Scripts/Editor/TMP_PreBuildProcessor.cs +++ b/Scripts/Editor/TMP_PreBuildProcessor.cs @@ -20,7 +20,7 @@ public void OnPreprocessBuild(BuildReport report) string fontAssetPath = AssetDatabase.GUIDToAssetPath(fontAssetGUIDs[i]); TMP_FontAsset fontAsset = AssetDatabase.LoadAssetAtPath(fontAssetPath); - if (fontAsset != null && fontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic && fontAsset.clearDynamicDataOnBuild && fontAsset.atlasTexture.width != 0) + if (fontAsset != null && (fontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic || fontAsset.atlasPopulationMode == AtlasPopulationMode.DynamicOS) && fontAsset.clearDynamicDataOnBuild && fontAsset.atlasTexture.width != 0) { //Debug.Log("Clearing [" + fontAsset.name + "] dynamic font asset data."); fontAsset.ClearFontAssetDataInternal(); diff --git a/Scripts/Editor/TMP_PreBuildProcessor.cs.meta b/Scripts/Editor/TMP_PreBuildProcessor.cs.meta index fc7859f..ac8e8ac 100644 --- a/Scripts/Editor/TMP_PreBuildProcessor.cs.meta +++ b/Scripts/Editor/TMP_PreBuildProcessor.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 06e104cf21d5fba46985b07321bbebe0 +guid: bf8befe3c500aa84b9ce3860a226cf41 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Scripts/Editor/TMP_ResourcesLoader.cs b/Scripts/Editor/TMP_ResourcesLoader.cs index 090bd77..5689b06 100644 --- a/Scripts/Editor/TMP_ResourcesLoader.cs +++ b/Scripts/Editor/TMP_ResourcesLoader.cs @@ -1,10 +1,54 @@ -using UnityEditor; +using System.Collections; +using UnityEditor; using UnityEngine; -using System.Collections; + namespace TMPro.EditorUtilities { + /* + [InitializeOnLoad] + class EssentialResourcesManager + { + private const string s_TMPShaderIncludeGUID = "407bc68d299748449bbf7f48ee690f8d"; + const string k_EssentialResourcesShaderVersionCheckKey = "TMP.EssentialResources.ShaderVersionCheck"; + + static EssentialResourcesManager() + { + bool shaderSearched = SessionState.GetBool(k_EssentialResourcesShaderVersionCheckKey, false); + + if (!EditorApplication.isPlayingOrWillChangePlaymode && !shaderSearched) + CheckShaderVersions(); + } + + static void CheckShaderVersions() + { + // Get path to TMP shader include file. + string assetPath = AssetDatabase.GUIDToAssetPath(s_TMPShaderIncludeGUID); + + if (string.IsNullOrEmpty(assetPath)) + return; + + AssetImporter importer = AssetImporter.GetAtPath(assetPath); + if (importer != null && string.IsNullOrEmpty(importer.userData)) + { + // Show Shader Import Window + TMP_EditorCoroutine.StartCoroutine(ShowShaderPackageImporterWindow()); + } + + SessionState.SetBool(k_EssentialResourcesShaderVersionCheckKey, true); + } + + static IEnumerator ShowShaderPackageImporterWindow() + { + yield return new WaitForSeconds(5.0f); + + TMP_ShaderPackageImporterWindow.ShowPackageImporterWindow(); + } + } + */ + + /* //[InitializeOnLoad] class TMP_ResourcesLoader { @@ -32,7 +76,6 @@ static TMP_ResourcesLoader() } - //[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] //static void OnBeforeSceneLoaded() //{ @@ -65,4 +108,5 @@ static TMP_ResourcesLoader() // } // } //} + */ } diff --git a/Scripts/Editor/TMP_SDFShaderGUI.cs b/Scripts/Editor/TMP_SDFShaderGUI.cs index ec6c6e6..dfd4cda 100644 --- a/Scripts/Editor/TMP_SDFShaderGUI.cs +++ b/Scripts/Editor/TMP_SDFShaderGUI.cs @@ -7,12 +7,14 @@ public class TMP_SDFShaderGUI : TMP_BaseShaderGUI { static ShaderFeature s_OutlineFeature, s_UnderlayFeature, s_BevelFeature, s_GlowFeature, s_MaskFeature; - static bool s_Face = true, s_Outline = true, s_Outline2, s_Underlay, s_Lighting, s_Glow, s_Bevel, s_Light, s_Bump, s_Env; + static bool s_Face = true, s_Outline = true, s_Outline2 = true, s_Outline3 = true, s_Underlay = true, s_Lighting = true, s_Glow, s_Bevel, s_Light, s_Bump, s_Env; static string[] s_FaceUVSpeedName = { "_FaceUVSpeed" }, s_FaceUvSpeedNames = { "_FaceUVSpeedX", "_FaceUVSpeedY" }, - s_OutlineUvSpeedNames = { "_OutlineUVSpeedX", "_OutlineUVSpeedY" }; + s_OutlineUvSpeedNames = { "_OutlineUVSpeedX", "_OutlineUVSpeedY" }, + s_OutlineUvSpeedName = { "_OutlineUVSpeed" }; + static TMP_SDFShaderGUI() { @@ -59,6 +61,8 @@ static TMP_SDFShaderGUI() protected override void DoGUI() { + bool isSRPMaterial = m_Material.HasProperty(ShaderUtilities.ID_IsoPerimeter); + s_Face = BeginPanel("Face", s_Face); if (s_Face) { @@ -67,76 +71,68 @@ protected override void DoGUI() EndPanel(); - s_Outline = m_Material.HasProperty(ShaderUtilities.ID_OutlineTex) ? BeginPanel("Outline", s_Outline) : BeginPanel("Outline", s_OutlineFeature, s_Outline); - if (s_Outline) + // Outline panels + if (isSRPMaterial) { - DoOutlinePanel(); + DoOutlinePanels(); } - - EndPanel(); - - if (m_Material.HasProperty(ShaderUtilities.ID_Outline2Color)) + else { - s_Outline2 = BeginPanel("Outline 2", s_OutlineFeature, s_Outline2); - if (s_Outline2) + s_Outline = m_Material.HasProperty(ShaderUtilities.ID_OutlineTex) ? BeginPanel("Outline", s_Outline) : BeginPanel("Outline", s_OutlineFeature, s_Outline); + if (s_Outline) { - DoOutline2Panel(); + DoOutlinePanel(); } EndPanel(); - } - if (m_Material.HasProperty(ShaderUtilities.ID_UnderlayColor)) - { - s_Underlay = BeginPanel("Underlay", s_UnderlayFeature, s_Underlay); - if (s_Underlay) - { - DoUnderlayPanel(); - } - - EndPanel(); - } - - if (m_Material.HasProperty("_SpecularColor")) - { - s_Lighting = BeginPanel("Lighting", s_BevelFeature, s_Lighting); - if (s_Lighting) + if (m_Material.HasProperty(ShaderUtilities.ID_Outline2Color)) { - s_Bevel = BeginPanel("Bevel", s_Bevel); - if (s_Bevel) + s_Outline2 = BeginPanel("Outline 2", s_OutlineFeature, s_Outline2); + if (s_Outline2) { - DoBevelPanel(); - } - - EndPanel(); - - s_Light = BeginPanel("Local Lighting", s_Light); - if (s_Light) - { - DoLocalLightingPanel(); + DoOutline2Panel(); } EndPanel(); + } + } - s_Bump = BeginPanel("Bump Map", s_Bump); - if (s_Bump) + // Underlay panel + if (m_Material.HasProperty(ShaderUtilities.ID_UnderlayColor)) + { + if (isSRPMaterial) + { + s_Underlay = BeginPanel("Underlay", s_Underlay); + if (s_Underlay) { - DoBumpMapPanel(); + DoUnderlayPanel(); } EndPanel(); - - s_Env = BeginPanel("Environment Map", s_Env); - if (s_Env) + } + else + { + s_Underlay = BeginPanel("Underlay", s_UnderlayFeature, s_Underlay); + if (s_Underlay) { - DoEnvMapPanel(); + DoUnderlayPanel(); } EndPanel(); } + } - EndPanel(); + // Lighting panel + if (m_Material.HasProperty("_SpecularColor")) + { + if (isSRPMaterial) + DrawLightingPanelSRP(); + else + DrawLightingPanelLegacy(); } + + else if (m_Material.HasProperty("_SpecColor")) { s_Bevel = BeginPanel("Bevel", s_Bevel); @@ -172,6 +168,7 @@ protected override void DoGUI() EndPanel(); } + if (m_Material.HasProperty(ShaderUtilities.ID_GlowColor)) { s_Glow = BeginPanel("Glow", s_GlowFeature, s_Glow); @@ -183,10 +180,88 @@ protected override void DoGUI() EndPanel(); } + s_DebugExtended = BeginPanel("Debug Settings", s_DebugExtended); if (s_DebugExtended) { - DoDebugPanel(); + if (isSRPMaterial) + DoDebugPanelSRP(); + else + DoDebugPanel(); + } + EndPanel(); + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + if (isSRPMaterial) + { + m_Editor.RenderQueueField(); + m_Editor.EnableInstancingField(); + m_Editor.DoubleSidedGIField(); + m_Editor.EmissionEnabledProperty(); + } + } + + private void DrawLightingPanelSRP() + { + s_Lighting = BeginPanel("Lighting", s_Lighting); + if (s_Lighting) + { + s_Bevel = BeginPanel("Bevel", s_Bevel); + if (s_Bevel) + { + DoBevelPanelSRP(); + } + EndPanel(); + + s_Light = BeginPanel("Local Lighting", s_Light); + if (s_Light) + { + DoLocalLightingPanel(); + } + EndPanel(); + } + + EndPanel(); + } + + private void DrawLightingPanelLegacy() + { + s_Lighting = BeginPanel("Lighting", s_BevelFeature, s_Lighting); + if (s_Lighting) + { + s_Bevel = BeginPanel("Bevel", s_Bevel); + if (s_Bevel) + { + DoBevelPanel(); + } + + EndPanel(); + + s_Light = BeginPanel("Local Lighting", s_Light); + if (s_Light) + { + DoLocalLightingPanel(); + } + + EndPanel(); + + s_Bump = BeginPanel("Bump Map", s_Bump); + if (s_Bump) + { + DoBumpMapPanel(); + } + + EndPanel(); + + s_Env = BeginPanel("Environment Map", s_Env); + if (s_Env) + { + DoEnvMapPanel(); + } + + EndPanel(); } EndPanel(); @@ -214,9 +289,9 @@ void DoFacePanel() } } - if (m_Material.HasProperty("_FaceSoftness")) + if (m_Material.HasProperty("_Softness")) { - DoSlider("_FaceSoftness", "X", "Softness"); + DoSlider("_Softness", "X", new Vector2(0, 1), "Softness"); } if (m_Material.HasProperty("_OutlineSoftness")) @@ -233,6 +308,11 @@ void DoFacePanel() } } + if (m_Material.HasProperty(ShaderUtilities.ID_IsoPerimeter)) + { + DoSlider("_IsoPerimeter", "X", new Vector2(-1, 1), "Dilate"); + } + EditorGUI.indentLevel -= 1; EditorGUILayout.Space(); } @@ -247,6 +327,10 @@ void DoOutlinePanel() { DoTexture2D("_OutlineTex", "Texture", true, s_OutlineUvSpeedNames); } + else if (m_Material.HasProperty("_OutlineUVSpeed")) + { + DoTexture2D("_OutlineTex", "Texture", true, s_OutlineUvSpeedName); + } else { DoTexture2D("_OutlineTex", "Texture", true); @@ -263,6 +347,69 @@ void DoOutlinePanel() EditorGUILayout.Space(); } + void DoOutlinePanel(int outlineID, string propertyField, string label) + { + EditorGUI.indentLevel += 1; + DoColor("_OutlineColor" + outlineID, label); + + if (outlineID != 3) + DoOffset("_OutlineOffset" + outlineID, "Offset"); + else + { + if (m_Material.GetFloat(ShaderUtilities.ID_OutlineMode) == 0) + DoOffset("_OutlineOffset" + outlineID, "Offset"); + } + + DoSlider("_Softness", propertyField, new Vector2(0, 1), "Softness"); + DoSlider("_IsoPerimeter", propertyField, new Vector2(-1, 1), "Dilate"); + + if (outlineID == 3) + { + DoToggle("_OutlineMode", "Outline Mode"); + } + + if (m_Material.HasProperty("_OutlineShininess")) + { + //DoSlider("_OutlineShininess", "Gloss"); + } + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + + void DoOutlinePanelWithTexture(int outlineID, string propertyField, string label) + { + EditorGUI.indentLevel += 1; + DoColor("_OutlineColor" + outlineID, label); + if (m_Material.HasProperty(ShaderUtilities.ID_OutlineTex)) + { + if (m_Material.HasProperty("_OutlineUVSpeedX")) + { + DoTexture2D("_OutlineTex", "Texture", true, s_OutlineUvSpeedNames); + } + else if (m_Material.HasProperty("_OutlineUVSpeed")) + { + DoTexture2D("_OutlineTex", "Texture", true, s_OutlineUvSpeedName); + } + else + { + DoTexture2D("_OutlineTex", "Texture", true); + } + } + + DoOffset("_OutlineOffset" + outlineID, "Offset"); + DoSlider("_Softness", propertyField, new Vector2(0, 1), "Softness"); + DoSlider("_IsoPerimeter", propertyField, new Vector2(-1, 1), "Dilate"); + + if (m_Material.HasProperty("_OutlineShininess")) + { + //DoSlider("_OutlineShininess", "Gloss"); + } + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + void DoOutline2Panel() { EditorGUI.indentLevel += 1; @@ -289,15 +436,49 @@ void DoOutline2Panel() EditorGUILayout.Space(); } + void DoOutlinePanels() + { + s_Outline = BeginPanel("Outline 1", s_Outline); + if (s_Outline) + DoOutlinePanelWithTexture(1, "Y", "Color"); + + EndPanel(); + + s_Outline2 = BeginPanel("Outline 2", s_Outline2); + if (s_Outline2) + DoOutlinePanel(2, "Z", "Color"); + + EndPanel(); + + s_Outline3 = BeginPanel("Outline 3", s_Outline3); + if (s_Outline3) + DoOutlinePanel(3, "W", "Color"); + + EndPanel(); + } + void DoUnderlayPanel() { EditorGUI.indentLevel += 1; - s_UnderlayFeature.DoPopup(m_Editor, m_Material); - DoColor("_UnderlayColor", "Color"); - DoSlider("_UnderlayOffsetX", "Offset X"); - DoSlider("_UnderlayOffsetY", "Offset Y"); - DoSlider("_UnderlayDilate", "Dilate"); - DoSlider("_UnderlaySoftness", "Softness"); + + if (m_Material.HasProperty(ShaderUtilities.ID_IsoPerimeter)) + { + DoColor("_UnderlayColor", "Color"); + DoSlider("_UnderlayOffset", "X", new Vector2(-1, 1), "Offset X"); + DoSlider("_UnderlayOffset", "Y", new Vector2(-1, 1), "Offset Y"); + DoSlider("_UnderlayDilate", new Vector2(-1, 1), "Dilate"); + DoSlider("_UnderlaySoftness", new Vector2(0, 1), "Softness"); + } + else + { + s_UnderlayFeature.DoPopup(m_Editor, m_Material); + DoColor("_UnderlayColor", "Color"); + DoSlider("_UnderlayOffsetX", "Offset X"); + DoSlider("_UnderlayOffsetY", "Offset Y"); + DoSlider("_UnderlayDilate", "Dilate"); + DoSlider("_UnderlaySoftness", "Softness"); + } + EditorGUI.indentLevel -= 1; EditorGUILayout.Space(); } @@ -321,6 +502,19 @@ void DoBevelPanel() EditorGUILayout.Space(); } + void DoBevelPanelSRP() + { + EditorGUI.indentLevel += 1; + DoPopup("_BevelType", "Type", s_BevelTypeLabels); + DoSlider("_BevelAmount", "Amount"); + DoSlider("_BevelOffset", "Offset"); + DoSlider("_BevelWidth", "Width"); + DoSlider("_BevelRoundness", "Roundness"); + DoSlider("_BevelClamp", "Clamp"); + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + void DoLocalLightingPanel() { EditorGUI.indentLevel += 1; @@ -460,6 +654,95 @@ void DoDebugPanel() EditorGUILayout.Space(); } + void DoDebugPanelSRP() + { + EditorGUI.indentLevel += 1; + DoTexture2D("_MainTex", "Font Atlas"); + DoFloat("_GradientScale", "Gradient Scale"); + //DoFloat("_TextureWidth", "Texture Width"); + //DoFloat("_TextureHeight", "Texture Height"); + EditorGUILayout.Space(); + + /* + DoFloat("_ScaleX", "Scale X"); + DoFloat("_ScaleY", "Scale Y"); + + if (m_Material.HasProperty(ShaderUtilities.ID_Sharpness)) + DoSlider("_Sharpness", "Sharpness"); + + DoSlider("_PerspectiveFilter", "Perspective Filter"); + EditorGUILayout.Space(); + DoFloat("_VertexOffsetX", "Offset X"); + DoFloat("_VertexOffsetY", "Offset Y"); + + if (m_Material.HasProperty(ShaderUtilities.ID_MaskCoord)) + { + EditorGUILayout.Space(); + s_MaskFeature.ReadState(m_Material); + s_MaskFeature.DoPopup(m_Editor, m_Material); + if (s_MaskFeature.Active) + { + DoMaskSubgroup(); + } + + EditorGUILayout.Space(); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + else if (m_Material.HasProperty("_MaskTex")) + { + DoMaskTexSubgroup(); + } + else if (m_Material.HasProperty(ShaderUtilities.ID_MaskSoftnessX)) + { + EditorGUILayout.Space(); + DoFloat("_MaskSoftnessX", "Softness X"); + DoFloat("_MaskSoftnessY", "Softness Y"); + DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels); + } + + if (m_Material.HasProperty(ShaderUtilities.ID_StencilID)) + { + EditorGUILayout.Space(); + DoFloat("_Stencil", "Stencil ID"); + DoFloat("_StencilComp", "Stencil Comp"); + } + + EditorGUILayout.Space(); + + EditorGUI.BeginChangeCheck(); + bool useRatios = EditorGUILayout.Toggle("Use Ratios", !m_Material.IsKeywordEnabled("RATIOS_OFF")); + if (EditorGUI.EndChangeCheck()) + { + m_Editor.RegisterPropertyChangeUndo("Use Ratios"); + if (useRatios) + { + m_Material.DisableKeyword("RATIOS_OFF"); + } + else + { + m_Material.EnableKeyword("RATIOS_OFF"); + } + } + */ + if (m_Material.HasProperty(ShaderUtilities.ShaderTag_CullMode)) + { + EditorGUILayout.Space(); + DoPopup("_CullMode", "Cull Mode", s_CullingTypeLabels); + } + + EditorGUILayout.Space(); + /* + EditorGUI.BeginDisabledGroup(true); + DoFloat("_ScaleRatioA", "Scale Ratio A"); + DoFloat("_ScaleRatioB", "Scale Ratio B"); + DoFloat("_ScaleRatioC", "Scale Ratio C"); + EditorGUI.EndDisabledGroup(); + */ + + EditorGUI.indentLevel -= 1; + EditorGUILayout.Space(); + } + void DoMaskSubgroup() { DoVector("_MaskCoord", "Mask Bounds", s_XywhVectorLabels); diff --git a/Scripts/Editor/TMP_SettingsEditor.cs b/Scripts/Editor/TMP_SettingsEditor.cs index f8c601e..fb36add 100644 --- a/Scripts/Editor/TMP_SettingsEditor.cs +++ b/Scripts/Editor/TMP_SettingsEditor.cs @@ -20,6 +20,7 @@ internal class Styles public static readonly GUIContent fallbackMaterialSettingsLabel = new GUIContent("Fallback Material Settings"); public static readonly GUIContent matchMaterialPresetLabel = new GUIContent("Match Material Presets"); + public static readonly GUIContent hideSubTextObjectsPresetLabel = new GUIContent("Hide Sub Text Objects", "Determines if sub text objects will be hidden in the scene hierarchy. Property change will only take effect after entering or existing play mode."); public static readonly GUIContent containerDefaultSettingsLabel = new GUIContent("Text Container Default Settings"); @@ -35,7 +36,7 @@ internal class Styles public static readonly GUIContent minLabel = new GUIContent("Min"); public static readonly GUIContent maxLabel = new GUIContent("Max"); - public static readonly GUIContent wordWrappingLabel = new GUIContent("Word Wrapping"); + public static readonly GUIContent textWrappingModeLabel = new GUIContent("Text Wrapping Mode"); public static readonly GUIContent kerningLabel = new GUIContent("Kerning"); public static readonly GUIContent extraPaddingLabel = new GUIContent("Extra Padding"); public static readonly GUIContent tintAllSpritesLabel = new GUIContent("Tint All Sprites"); @@ -46,6 +47,7 @@ internal class Styles public static readonly GUIContent dynamicAtlasTextureGroup = new GUIContent("Dynamic Atlas Texture Group"); public static readonly GUIContent missingGlyphLabel = new GUIContent("Missing Character Unicode", "The character to be displayed when the requested character is not found in any font asset or fallbacks."); + public static readonly GUIContent clearDynamicDataOnBuildLabel = new GUIContent("Clear Dynamic Data On Build", "Determines if the \"Clear Dynamic Data on Build\" property will be set to true or false on newly created dynamic font assets."); public static readonly GUIContent disableWarningsLabel = new GUIContent("Disable warnings", "Disable warning messages in the Console."); public static readonly GUIContent defaultSpriteAssetLabel = new GUIContent("Default Sprite Asset", "The Sprite Asset that will be assigned by default when using the tag when no Sprite Asset is specified."); @@ -90,12 +92,14 @@ internal class Styles SerializedProperty m_PropColorGradientPresetsPath; SerializedProperty m_PropMatchMaterialPreset; - SerializedProperty m_PropWordWrapping; + SerializedProperty m_PropHideSubTextObjects; + SerializedProperty m_PropTextWrappingMode; SerializedProperty m_PropKerning; SerializedProperty m_PropExtraPadding; SerializedProperty m_PropTintAllSprites; SerializedProperty m_PropParseEscapeCharacters; SerializedProperty m_PropMissingGlyphCharacter; + SerializedProperty m_PropClearDynamicDataOnBuild; //SerializedProperty m_DynamicAtlasTextureManager; SerializedProperty m_GetFontFeaturesAtRuntime; @@ -107,6 +111,7 @@ internal class Styles SerializedProperty m_PropUseModernHangulLineBreakingRules; private const string k_UndoRedo = "UndoRedoPerformed"; + private bool m_IsFallbackGlyphCacheDirty; public void OnEnable() { @@ -150,15 +155,21 @@ public void OnEnable() EditorGUI.LabelField(rect, Styles.fallbackFontAssetsListLabel); }; + m_List.onReorderCallback = itemList => + { + m_IsFallbackGlyphCacheDirty = true; + }; + m_PropMatchMaterialPreset = serializedObject.FindProperty("m_matchMaterialPreset"); + m_PropHideSubTextObjects = serializedObject.FindProperty("m_HideSubTextObjects"); - m_PropWordWrapping = serializedObject.FindProperty("m_enableWordWrapping"); + m_PropTextWrappingMode = serializedObject.FindProperty("m_TextWrappingMode"); m_PropKerning = serializedObject.FindProperty("m_enableKerning"); m_PropExtraPadding = serializedObject.FindProperty("m_enableExtraPadding"); m_PropTintAllSprites = serializedObject.FindProperty("m_enableTintAllSprites"); m_PropParseEscapeCharacters = serializedObject.FindProperty("m_enableParseEscapeCharacters"); m_PropMissingGlyphCharacter = serializedObject.FindProperty("m_missingGlyphCharacter"); - + m_PropClearDynamicDataOnBuild = serializedObject.FindProperty("m_ClearDynamicDataOnBuild"); m_PropWarningsDisabled = serializedObject.FindProperty("m_warningsDisabled"); //m_DynamicAtlasTextureManager = serializedObject.FindProperty("m_DynamicAtlasTextureGroup"); @@ -173,6 +184,7 @@ public override void OnInspectorGUI() { serializedObject.Update(); string evt_cmd = Event.current.commandName; + m_IsFallbackGlyphCacheDirty = false; float labelWidth = EditorGUIUtility.labelWidth; float fieldWidth = EditorGUIUtility.fieldWidth; @@ -184,7 +196,11 @@ public override void OnInspectorGUI() EditorGUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.Label(Styles.defaultFontAssetLabel, EditorStyles.boldLabel); EditorGUI.indentLevel = 1; + EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_PropFontAsset, Styles.defaultFontAssetLabel); + if (EditorGUI.EndChangeCheck()) + m_IsFallbackGlyphCacheDirty = true; + EditorGUILayout.PropertyField(m_PropDefaultFontAssetPath, Styles.defaultFontAssetPathLabel); EditorGUI.indentLevel = 0; @@ -194,11 +210,15 @@ public override void OnInspectorGUI() // FALLBACK FONT ASSETs EditorGUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.Label(Styles.fallbackFontAssetsLabel, EditorStyles.boldLabel); + EditorGUI.BeginChangeCheck(); m_List.DoLayoutList(); + if (EditorGUI.EndChangeCheck()) + m_IsFallbackGlyphCacheDirty = true; GUILayout.Label(Styles.fallbackMaterialSettingsLabel, EditorStyles.boldLabel); EditorGUI.indentLevel = 1; EditorGUILayout.PropertyField(m_PropMatchMaterialPreset, Styles.matchMaterialPresetLabel); + EditorGUILayout.PropertyField(m_PropHideSubTextObjects, Styles.hideSubTextObjectsPresetLabel); EditorGUI.indentLevel = 0; EditorGUILayout.Space(); @@ -210,6 +230,7 @@ public override void OnInspectorGUI() EditorGUI.indentLevel = 1; EditorGUILayout.PropertyField(m_GetFontFeaturesAtRuntime, Styles.getFontFeaturesAtRuntime); EditorGUILayout.PropertyField(m_PropMissingGlyphCharacter, Styles.missingGlyphLabel); + EditorGUILayout.PropertyField(m_PropClearDynamicDataOnBuild, Styles.clearDynamicDataOnBuildLabel); EditorGUILayout.PropertyField(m_PropWarningsDisabled, Styles.disableWarningsLabel); //EditorGUILayout.PropertyField(m_DynamicAtlasTextureManager, Styles.dynamicAtlasTextureManager); EditorGUI.indentLevel = 0; @@ -251,7 +272,7 @@ public override void OnInspectorGUI() EditorGUIUtility.labelWidth = labelWidth; EditorGUIUtility.fieldWidth = fieldWidth; - EditorGUILayout.PropertyField(m_PropWordWrapping, Styles.wordWrappingLabel); + EditorGUILayout.PropertyField(m_PropTextWrappingMode, Styles.textWrappingModeLabel); EditorGUILayout.PropertyField(m_PropKerning, Styles.kerningLabel); EditorGUILayout.PropertyField(m_PropExtraPadding, Styles.extraPaddingLabel); @@ -268,7 +289,11 @@ public override void OnInspectorGUI() EditorGUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.Label(Styles.defaultSpriteAssetLabel, EditorStyles.boldLabel); EditorGUI.indentLevel = 1; + EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_PropSpriteAsset, Styles.defaultSpriteAssetLabel); + if (EditorGUI.EndChangeCheck()) + m_IsFallbackGlyphCacheDirty = true; + EditorGUILayout.PropertyField(m_PropMissingSpriteCharacterUnicode, Styles.missingSpriteCharacterUnicodeLabel); EditorGUILayout.PropertyField(m_PropEnableEmojiSupport, Styles.enableEmojiSupportLabel); //EditorGUILayout.PropertyField(m_PropSpriteRelativeScaling, Styles.spriteRelativeScale); @@ -324,6 +349,9 @@ public override void OnInspectorGUI() EditorGUILayout.Space(); EditorGUILayout.EndVertical(); + if (m_IsFallbackGlyphCacheDirty) + TMP_ResourceManager.RebuildFontAssetCache(); + if (serializedObject.ApplyModifiedProperties() || evt_cmd == k_UndoRedo) { EditorUtility.SetDirty(target); diff --git a/Scripts/Editor/TMP_SpriteAssetEditor.cs b/Scripts/Editor/TMP_SpriteAssetEditor.cs index 749c2da..a8997f6 100644 --- a/Scripts/Editor/TMP_SpriteAssetEditor.cs +++ b/Scripts/Editor/TMP_SpriteAssetEditor.cs @@ -73,7 +73,7 @@ public void OnEnable() m_DescentLineProperty = m_FaceInfoProperty.FindPropertyRelative("m_DescentLine"); m_spriteAtlas_prop = serializedObject.FindProperty("spriteSheet"); - m_material_prop = serializedObject.FindProperty("material"); + m_material_prop = serializedObject.FindProperty("m_Material"); m_SpriteCharacterTableProperty = serializedObject.FindProperty("m_SpriteCharacterTable"); m_SpriteGlyphTableProperty = serializedObject.FindProperty("m_SpriteGlyphTable"); @@ -648,7 +648,10 @@ public override void OnInspectorGUI() if (serializedObject.ApplyModifiedProperties() || evt_cmd == k_UndoRedo || isAssetDirty) { if (m_SpriteAsset.m_IsSpriteAssetLookupTablesDirty || evt_cmd == k_UndoRedo) + { m_SpriteAsset.UpdateLookupTables(); + TMP_ResourceManager.RebuildFontAssetCache(); + } TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, m_SpriteAsset); diff --git a/Scripts/Editor/TMP_SpriteAssetMenu.cs b/Scripts/Editor/TMP_SpriteAssetMenu.cs index 657c3de..2216d26 100644 --- a/Scripts/Editor/TMP_SpriteAssetMenu.cs +++ b/Scripts/Editor/TMP_SpriteAssetMenu.cs @@ -180,11 +180,11 @@ public static void CreateSpriteAsset() string fileNameWithExtension = Path.GetFileName(filePathWithName); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePathWithName); string filePath = filePathWithName.Replace(fileNameWithExtension, ""); - + string uniquePath = AssetDatabase.GenerateUniqueAssetPath(filePath + fileNameWithoutExtension + ".asset"); // Create new Sprite Asset TMP_SpriteAsset spriteAsset = ScriptableObject.CreateInstance(); - AssetDatabase.CreateAsset(spriteAsset, filePath + fileNameWithoutExtension + ".asset"); + AssetDatabase.CreateAsset(spriteAsset, uniquePath); spriteAsset.version = "1.1.0"; diff --git a/Scripts/Editor/TMPro_ContextMenus.cs b/Scripts/Editor/TMPro_ContextMenus.cs index 12a08ec..dd3b4f7 100644 --- a/Scripts/Editor/TMPro_ContextMenus.cs +++ b/Scripts/Editor/TMPro_ContextMenus.cs @@ -193,11 +193,22 @@ static void ResetSettings(MenuCommand command) ShaderUtilities.GetShaderPropertyIDs(); // Make sure we have valid Property IDs if (mat.HasProperty(ShaderUtilities.ID_GradientScale)) { + bool isSRPShader = mat.HasProperty(ShaderUtilities.ID_IsoPerimeter); + // Copy unique properties of the SDF Material var texture = mat.GetTexture(ShaderUtilities.ID_MainTex); var gradientScale = mat.GetFloat(ShaderUtilities.ID_GradientScale); - var texWidth = mat.GetFloat(ShaderUtilities.ID_TextureWidth); - var texHeight = mat.GetFloat(ShaderUtilities.ID_TextureHeight); + + float texWidth = 0, texHeight = 0; + float normalWeight = 0, boldWeight = 0; + + if (!isSRPShader) + { + texWidth = mat.GetFloat(ShaderUtilities.ID_TextureWidth); + texHeight = mat.GetFloat(ShaderUtilities.ID_TextureHeight); + normalWeight = mat.GetFloat(ShaderUtilities.ID_WeightNormal); + boldWeight = mat.GetFloat(ShaderUtilities.ID_WeightBold); + } var stencilId = 0.0f; var stencilComp = 0.0f; @@ -208,9 +219,6 @@ static void ResetSettings(MenuCommand command) stencilComp = mat.GetFloat(ShaderUtilities.ID_StencilComp); } - var normalWeight = mat.GetFloat(ShaderUtilities.ID_WeightNormal); - var boldWeight = mat.GetFloat(ShaderUtilities.ID_WeightBold); - // Reset the material Unsupported.SmartReset(mat); @@ -220,17 +228,20 @@ static void ResetSettings(MenuCommand command) // Copy unique material properties back to the material. mat.SetTexture(ShaderUtilities.ID_MainTex, texture); mat.SetFloat(ShaderUtilities.ID_GradientScale, gradientScale); - mat.SetFloat(ShaderUtilities.ID_TextureWidth, texWidth); - mat.SetFloat(ShaderUtilities.ID_TextureHeight, texHeight); + + if (!isSRPShader) + { + mat.SetFloat(ShaderUtilities.ID_TextureWidth, texWidth); + mat.SetFloat(ShaderUtilities.ID_TextureHeight, texHeight); + mat.SetFloat(ShaderUtilities.ID_WeightNormal, normalWeight); + mat.SetFloat(ShaderUtilities.ID_WeightBold, boldWeight); + } if (mat.HasProperty(ShaderUtilities.ID_StencilID)) { mat.SetFloat(ShaderUtilities.ID_StencilID, stencilId); mat.SetFloat(ShaderUtilities.ID_StencilComp, stencilComp); } - - mat.SetFloat(ShaderUtilities.ID_WeightNormal, normalWeight); - mat.SetFloat(ShaderUtilities.ID_WeightBold, boldWeight); } else { @@ -379,6 +390,8 @@ static void ClearFontAssetData(MenuCommand command) fontAsset.ClearFontAssetData(true); + TMP_ResourceManager.RebuildFontAssetCache(); + TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset); } diff --git a/Scripts/Editor/TMPro_CreateObjectMenu.cs b/Scripts/Editor/TMPro_CreateObjectMenu.cs index b614f12..9525565 100644 --- a/Scripts/Editor/TMPro_CreateObjectMenu.cs +++ b/Scripts/Editor/TMPro_CreateObjectMenu.cs @@ -2,11 +2,8 @@ using UnityEditor; using UnityEditor.Presets; using UnityEditor.SceneManagement; - -//#if !UNITY_2021_2_OR_NEWER using UnityEditor.Experimental.SceneManagement; -//#endif - +using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityEngine.EventSystems; @@ -33,9 +30,21 @@ static void CreateTextMeshProObjectPerform(MenuCommand command) if (textComponent.m_isWaitingOnResourceLoad == false) { // Get reference to potential Presets for component + #if UNITY_2019_3_OR_NEWER Preset[] presets = Preset.GetDefaultPresetsForObject(textComponent); if (presets == null || presets.Length == 0) + { + textComponent.text = "Sample text"; + textComponent.alignment = TextAlignmentOptions.TopLeft; + } + else + { + textComponent.renderer.sortingLayerID = textComponent._SortingLayerID; + textComponent.renderer.sortingOrder = textComponent._SortingOrder; + } + #else + if (Preset.GetDefaultForObject(textComponent) == null) { textComponent.text = "Sample text"; textComponent.alignment = TextAlignmentOptions.TopLeft; @@ -45,6 +54,7 @@ static void CreateTextMeshProObjectPerform(MenuCommand command) textComponent.renderer.sortingLayerID = textComponent._SortingLayerID; textComponent.renderer.sortingOrder = textComponent._SortingOrder; } + #endif if (TMP_Settings.autoSizeTextContainer) { @@ -90,6 +100,7 @@ static void CreateTextMeshProGuiObjectPerform(MenuCommand menuCommand) if (textComponent.m_isWaitingOnResourceLoad == false) { // Get reference to potential Presets for component + #if UNITY_2019_3_OR_NEWER Preset[] presets = Preset.GetDefaultPresetsForObject(textComponent); if (presets == null || presets.Length == 0) @@ -98,6 +109,14 @@ static void CreateTextMeshProGuiObjectPerform(MenuCommand menuCommand) textComponent.color = Color.white; textComponent.text = "New Text"; } + #else + if (Preset.GetDefaultForObject(textComponent) == null) + { + textComponent.fontSize = TMP_Settings.defaultFontSize; + textComponent.color = Color.white; + textComponent.text = "New Text"; + } + #endif if (TMP_Settings.autoSizeTextContainer) { diff --git a/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs b/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs index 0f89c96..e82fcf1 100644 --- a/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs +++ b/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs @@ -27,7 +27,7 @@ public static void ShowFontAtlasCreatorWindow() } - public static void ShowFontAtlasCreatorWindow(Font sourceFontFile) + public static void ShowFontAtlasCreatorWindow(Font font) { var window = GetWindow(); @@ -39,7 +39,7 @@ public static void ShowFontAtlasCreatorWindow(Font sourceFontFile) window.m_SelectedFontAsset = null; // Override selected font asset - window.m_SourceFontFile = sourceFontFile; + window.m_SourceFont = font; // Make sure TMP Essential Resources have been imported. window.CheckEssentialResources(); @@ -73,7 +73,7 @@ public static void ShowFontAtlasCreatorWindow(TMP_FontAsset fontAsset) else { window.m_WarningMessage = "Font Asset [" + fontAsset.name + "] does not contain any previous \"Font Asset Creation Settings\". This usually means [" + fontAsset.name + "] was created before this new functionality was added."; - window.m_SourceFontFile = null; + window.m_SourceFont = null; window.m_LegacyFontAsset = fontAsset; } @@ -125,7 +125,6 @@ enum FontPackingModes { Fast = 0, Optimum = 4 }; float m_AtlasGenerationProgress; string m_AtlasGenerationProgressLabel = string.Empty; - float m_RenderingProgress; bool m_IsGlyphPackingDone; bool m_IsGlyphRenderingDone; bool m_IsRenderingDone; @@ -134,16 +133,22 @@ enum FontPackingModes { Fast = 0, Optimum = 4 }; bool m_IsGenerationCancelled; bool m_IsFontAtlasInvalid; - Object m_SourceFontFile; + Font m_SourceFont; + int m_SourceFontFaceIndex; + private string[] m_SourceFontFaces = new string[0]; TMP_FontAsset m_SelectedFontAsset; TMP_FontAsset m_LegacyFontAsset; TMP_FontAsset m_ReferencedFontAsset; TextAsset m_CharactersFromFile; int m_PointSize; - int m_Padding = 5; - //FaceStyles m_FontStyle = FaceStyles.Normal; - //float m_FontStyleValue = 2; + float m_PaddingFieldValue = 10; + int m_Padding; + + enum PaddingMode { Undefined = 0, Percentage = 1, Pixel = 2 }; + + string[] k_PaddingOptionLabels = { "%", "px" }; + private PaddingMode m_PaddingMode = PaddingMode.Percentage; GlyphRenderMode m_GlyphRenderMode = GlyphRenderMode.SDFAA; int m_AtlasWidth = 512; @@ -201,6 +206,10 @@ public void OnEnable() } } + // Get potential font face and styles for the current font. + if (m_SourceFont != null) + m_SourceFontFaces = GetFontFaces(); + ClearGeneratedData(); } @@ -423,14 +432,27 @@ void DrawControls() // Disable Options if already generating a font atlas texture. EditorGUI.BeginDisabledGroup(m_IsProcessing); { - // FONT TTF SELECTION + // FONT SELECTION + EditorGUI.BeginChangeCheck(); + m_SourceFont = EditorGUILayout.ObjectField("Source Font", m_SourceFont, typeof(Font), false) as Font; + if (EditorGUI.EndChangeCheck()) + { + m_SelectedFontAsset = null; + m_IsFontAtlasInvalid = true; + m_SourceFontFaces = GetFontFaces(); + m_SourceFontFaceIndex = 0; + } + + // FONT FACE AND STYLE SELECTION EditorGUI.BeginChangeCheck(); - m_SourceFontFile = EditorGUILayout.ObjectField("Source Font File", m_SourceFontFile, typeof(Font), false) as Font; + GUI.enabled = m_SourceFont != null; + m_SourceFontFaceIndex = EditorGUILayout.Popup("Font Face", m_SourceFontFaceIndex, m_SourceFontFaces); if (EditorGUI.EndChangeCheck()) { m_SelectedFontAsset = null; m_IsFontAtlasInvalid = true; } + GUI.enabled = true; // FONT SIZING EditorGUI.BeginChangeCheck(); @@ -451,13 +473,23 @@ void DrawControls() } // FONT PADDING + GUILayout.BeginHorizontal(); EditorGUI.BeginChangeCheck(); - m_Padding = EditorGUILayout.IntField("Padding", m_Padding); - m_Padding = (int)Mathf.Clamp(m_Padding, 0f, 64f); + + m_PaddingFieldValue = EditorGUILayout.FloatField("Padding", m_PaddingFieldValue); + + int selection = m_PaddingMode == PaddingMode.Undefined || m_PaddingMode == PaddingMode.Pixel ? 1 : 0; + selection = GUILayout.SelectionGrid(selection, k_PaddingOptionLabels, 2); + + if (m_PaddingMode == PaddingMode.Percentage) + m_PaddingFieldValue = (int)(m_PaddingFieldValue + 0.5f); + if (EditorGUI.EndChangeCheck()) { + m_PaddingMode = (PaddingMode)selection + 1; m_IsFontAtlasInvalid = true; } + GUILayout.EndHorizontal(); // FONT PACKING METHOD SELECTION EditorGUI.BeginChangeCheck(); @@ -666,10 +698,10 @@ void DrawControls() EditorGUILayout.HelpBox(m_WarningMessage, MessageType.Warning); } - GUI.enabled = m_SourceFontFile != null && !m_IsProcessing && !m_IsGenerationDisabled; // Enable Preview if we are not already rendering a font. + GUI.enabled = m_SourceFont != null && !m_IsProcessing && !m_IsGenerationDisabled; // Enable Preview if we are not already rendering a font. if (GUILayout.Button("Generate Font Atlas") && GUI.enabled) { - if (!m_IsProcessing && m_SourceFontFile != null) + if (!m_IsProcessing && m_SourceFont != null) { DestroyImmediate(m_FontAtlasTexture); DestroyImmediate(m_GlyphRectPreviewTexture); @@ -684,16 +716,13 @@ void DrawControls() Debug.Log("Font Asset Creator - Error [" + errorCode + "] has occurred while Initializing the FreeType Library."); } - // Get file path of the source font file. - string fontPath = AssetDatabase.GetAssetPath(m_SourceFontFile); - if (errorCode == FontEngineError.Success) { - errorCode = FontEngine.LoadFontFace(fontPath); + errorCode = FontEngine.LoadFontFace(m_SourceFont, 0, m_SourceFontFaceIndex); if (errorCode != FontEngineError.Success) { - Debug.Log("Font Asset Creator - Error Code [" + errorCode + "] has occurred trying to load the [" + m_SourceFontFile.name + "] font file. This typically results from the use of an incompatible or corrupted font file.", m_SourceFontFile); + Debug.Log("Font Asset Creator - Error Code [" + errorCode + "] has occurred trying to load the [" + m_SourceFont.name + "] font file. This typically results from the use of an incompatible or corrupted font file.", m_SourceFont); } } @@ -823,6 +852,8 @@ void DrawControls() FontEngine.SetFaceSize(m_PointSize); + m_Padding = (int)(m_PaddingMode == PaddingMode.Percentage ? m_PointSize * m_PaddingFieldValue / 100f : m_PaddingFieldValue); + m_GlyphsToPack.Clear(); m_GlyphsPacked.Clear(); @@ -893,6 +924,8 @@ void DrawControls() // Set point size FontEngine.SetFaceSize(m_PointSize); + m_Padding = (int)(m_PaddingMode == PaddingMode.Percentage ? m_PointSize * m_PaddingFieldValue / 100 : m_PaddingFieldValue); + m_GlyphsToPack.Clear(); m_GlyphsPacked.Clear(); @@ -936,6 +969,8 @@ void DrawControls() FontEngine.SetFaceSize(m_PointSize); + m_Padding = (int)(m_PaddingMode == PaddingMode.Percentage ? m_PointSize * m_PaddingFieldValue / 100 : m_PaddingFieldValue); + m_GlyphsToPack.Clear(); m_GlyphsPacked.Clear(); @@ -960,15 +995,15 @@ void DrawControls() int upSampling = 1; switch (m_GlyphRenderMode) { - case GlyphRenderMode.SDF8: - upSampling = 8; - break; - case GlyphRenderMode.SDF16: - upSampling = 16; - break; - case GlyphRenderMode.SDF32: - upSampling = 32; - break; + case GlyphRenderMode.SDF8: + upSampling = 8; + break; + case GlyphRenderMode.SDF16: + upSampling = 16; + break; + case GlyphRenderMode.SDF32: + upSampling = 32; + break; } Debug.Log("Glyph rendering has been aborted due to sampling point size of [" + m_PointSize + "] x SDF [" + upSampling + "] up sampling exceeds 16,384 point size. Please revise your generation settings to make sure the sampling point size x SDF up sampling mode does not exceed 16,384."); @@ -1092,7 +1127,7 @@ void DrawControls() if (m_LegacyFontAsset != null) SaveNewFontAssetWithSameName(m_LegacyFontAsset); else - SaveNewFontAsset(m_SourceFontFile); + SaveNewFontAsset(m_SourceFont); } else { @@ -1109,7 +1144,7 @@ void DrawControls() { if (m_SelectedFontAsset == null) { - SaveNewFontAsset(m_SourceFontFile); + SaveNewFontAsset(m_SourceFont); } else { @@ -1162,6 +1197,16 @@ void ClearGeneratedData() m_WarningMessage = string.Empty; } + /// + /// + /// + /// + string[] GetFontFaces() + { + FontEngine.LoadFontFace(m_SourceFont, 0, 0); + return FontEngine.GetFontFaces(); + } + /// /// Function to update the feedback window showing the results of the latest generation. @@ -1348,12 +1393,6 @@ void Save_Bitmap_FontAsset(string filePath) string dataPath = Application.dataPath; - if (filePath.IndexOf(dataPath, System.StringComparison.InvariantCultureIgnoreCase) == -1) - { - Debug.LogError("You're saving the font asset in a directory outside of this project folder. This is not supported. Please select a directory under \"" + dataPath + "\""); - return; - } - string relativeAssetPath = filePath.Substring(dataPath.Length - 6); string tex_DirName = Path.GetDirectoryName(relativeAssetPath); string tex_FileName = Path.GetFileNameWithoutExtension(relativeAssetPath); @@ -1374,8 +1413,8 @@ void Save_Bitmap_FontAsset(string filePath) fontAsset.atlasRenderMode = m_GlyphRenderMode; // Reference to the source font file GUID. - fontAsset.m_SourceFontFile_EditorRef = (Font)m_SourceFontFile; - fontAsset.m_SourceFontFileGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_SourceFontFile)); + fontAsset.m_SourceFontFile_EditorRef = m_SourceFont; + fontAsset.m_SourceFontFileGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_SourceFont)); // Add FaceInfo to Font Asset fontAsset.faceInfo = m_FaceInfo; @@ -1404,7 +1443,7 @@ void Save_Bitmap_FontAsset(string filePath) AssetDatabase.AddObjectToAsset(m_FontAtlasTexture, fontAsset); // Create new Material and Add it as Sub-Asset - Shader default_Shader = Shader.Find("TextMeshPro/Bitmap"); // m_shaderSelection; + Shader default_Shader = Shader.Find("TextMeshPro/Bitmap"); Material tmp_material = new Material(default_Shader); tmp_material.name = tex_FileName + " Material"; tmp_material.SetTexture(ShaderUtilities.ID_MainTex, m_FontAtlasTexture); @@ -1467,7 +1506,12 @@ void Save_Bitmap_FontAsset(string filePath) if (tex.width != m_AtlasWidth || tex.height != m_AtlasHeight) { + #if UNITY_2021_2_OR_NEWER + tex.Reinitialize(m_AtlasWidth, m_AtlasHeight); + #else tex.Resize(m_AtlasWidth, m_AtlasHeight); + #endif + tex.Apply(false); } @@ -1529,12 +1573,6 @@ void Save_SDF_FontAsset(string filePath) string dataPath = Application.dataPath; - if (filePath.IndexOf(dataPath, System.StringComparison.InvariantCultureIgnoreCase) == -1) - { - Debug.LogError("You're saving the font asset in a directory outside of this project folder. This is not supported. Please select a directory under \"" + dataPath + "\""); - return; - } - string relativeAssetPath = filePath.Substring(dataPath.Length - 6); string tex_DirName = Path.GetDirectoryName(relativeAssetPath); string tex_FileName = Path.GetFileNameWithoutExtension(relativeAssetPath); @@ -1553,8 +1591,8 @@ void Save_SDF_FontAsset(string filePath) fontAsset.version = "1.1.0"; // Reference to source font file GUID. - fontAsset.m_SourceFontFile_EditorRef = (Font)m_SourceFontFile; - fontAsset.m_SourceFontFileGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_SourceFontFile)); + fontAsset.m_SourceFontFile_EditorRef = m_SourceFont; + fontAsset.m_SourceFontFileGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_SourceFont)); //Set Font Asset Type fontAsset.atlasRenderMode = m_GlyphRenderMode; @@ -1658,7 +1696,12 @@ void Save_SDF_FontAsset(string filePath) if (tex.width != m_AtlasWidth || tex.height != m_AtlasHeight) { + #if UNITY_2021_2_OR_NEWER + tex.Reinitialize(m_AtlasWidth, m_AtlasHeight); + #else tex.Resize(m_AtlasWidth, m_AtlasHeight); + #endif + tex.Apply(false); } @@ -1726,10 +1769,12 @@ FontAssetCreationSettings SaveFontCreationSettings() FontAssetCreationSettings settings = new FontAssetCreationSettings(); //settings.sourceFontFileName = m_SourceFontFile.name; - settings.sourceFontFileGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_SourceFontFile)); + settings.sourceFontFileGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_SourceFont)); + settings.faceIndex = m_SourceFontFaceIndex; settings.pointSizeSamplingMode = m_PointSizeSamplingMode; settings.pointSize = m_PointSize; settings.padding = m_Padding; + settings.paddingMode = (int)m_PaddingMode; settings.packingMode = (int)m_PackingMode; settings.atlasWidth = m_AtlasWidth; settings.atlasHeight = m_AtlasHeight; @@ -1737,8 +1782,6 @@ FontAssetCreationSettings SaveFontCreationSettings() settings.characterSequence = m_CharacterSequence; settings.referencedFontAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_ReferencedFontAsset)); settings.referencedTextAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_CharactersFromFile)); - //settings.fontStyle = (int)m_FontStyle; - //settings.fontStyleModifier = m_FontStyleValue; settings.renderMode = (int)m_GlyphRenderMode; settings.includeFontFeatures = m_IncludeFontFeatures; @@ -1752,10 +1795,13 @@ FontAssetCreationSettings SaveFontCreationSettings() /// void LoadFontCreationSettings(FontAssetCreationSettings settings) { - m_SourceFontFile = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(settings.sourceFontFileGUID)); + m_SourceFont = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(settings.sourceFontFileGUID)); + m_SourceFontFaceIndex = settings.faceIndex; m_PointSizeSamplingMode = settings.pointSizeSamplingMode; m_PointSize = settings.pointSize; m_Padding = settings.padding; + m_PaddingMode = settings.paddingMode == 0 ? PaddingMode.Pixel : (PaddingMode)settings.paddingMode; + m_PaddingFieldValue = m_PaddingMode == PaddingMode.Percentage ? (float)m_Padding / m_PointSize * 100 : m_Padding; m_PackingMode = (FontPackingModes)settings.packingMode; m_AtlasWidth = settings.atlasWidth; m_AtlasHeight = settings.atlasHeight; @@ -1763,8 +1809,6 @@ void LoadFontCreationSettings(FontAssetCreationSettings settings) m_CharacterSequence = settings.characterSequence; m_ReferencedFontAsset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(settings.referencedFontAssetGUID)); m_CharactersFromFile = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(settings.referencedTextAssetGUID)); - //m_FontStyle = (FaceStyles)settings.fontStyle; - //m_FontStyleValue = settings.fontStyleModifier; m_GlyphRenderMode = (GlyphRenderMode)settings.renderMode; m_IncludeFontFeatures = settings.includeFontFeatures; } diff --git a/Scripts/Editor/TMPro_FontPlugin.cs b/Scripts/Editor/TMPro_FontPlugin.cs deleted file mode 100644 index 3b098ff..0000000 --- a/Scripts/Editor/TMPro_FontPlugin.cs +++ /dev/null @@ -1,115 +0,0 @@ -using UnityEngine; -using UnityEditor; -using System.Collections; -using System; -using System.Runtime.InteropServices; - - -namespace TMPro.EditorUtilities -{ - /* - public class TMPro_FontPlugin - { - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - private delegate void DebugLog(string log); - private static readonly DebugLog debugLog = DebugWrapper; - private static readonly IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(debugLog); - - private static void DebugWrapper(string log) - { - Debug.Log(log); - } - - public static void LinkDebugLog() - { - LinkDebug(functionPointer); - } - - [DllImport("TMPro_Plugin")] - private static extern void LinkDebug([MarshalAs(UnmanagedType.FunctionPtr)]IntPtr debugCall); - - [DllImport("TMPro_Plugin")] - public static extern - int Initialize_FontEngine(); - - [DllImport("TMPro_Plugin")] - public static extern - int Destroy_FontEngine(); - - [DllImport("TMPro_Plugin")] - public static extern - int Load_TrueType_Font(string fontPath); - - [DllImport("TMPro_Plugin")] - public static extern - int FT_Size_Font(int fontSize); - - [DllImport("TMPro_Plugin")] - public static extern - int Render_Character(byte[] buffer_fill, byte[] buffer_edge, int buffer_width, int buffer_height, int offset, int asc, FaceStyles style, float thickness, RenderModes rasterMode, ref FT_GlyphInfo glyphInfo); - - [DllImport("TMPro_Plugin")] - public static extern - int Render_Characters(byte[] buffer, int buffer_width, int buffer_height, int character_padding, int[] asc_set, int char_count, FaceStyles style, float style_mod, bool autoSize, RenderModes renderMode, int method, ref FT_FaceInfo fontData, FT_GlyphInfo[] Output); - - [DllImport("TMPro_Plugin")] - public static extern - int FT_GetKerningPairs(string fontPath, int[] characterSet, int setCount, FT_KerningPair[] kerningPairs); - - [DllImport("TMPro_Plugin")] - public static extern - float Check_RenderProgress(); - - [DllImport("TMPro_Plugin")] - internal static extern - void SendCancellationRequest(CancellationRequestType request); - } - - public enum FaceStyles { Normal, Bold, Italic, Bold_Italic, Outline, Bold_Sim }; - public enum RenderModes { HintedSmooth = 0, Smooth = 1, RasterHinted = 2, Raster = 3, DistanceField16 = 6, DistanceField32 = 7 }; // SignedDistanceField64 = 8 - - internal enum CancellationRequestType : byte { None = 0x0, CancelInProgess = 0x1, WindowClosed = 0x2 }; - - [StructLayout(LayoutKind.Sequential)] - public struct FT_KerningPair - { - public int ascII_Left; - public int ascII_Right; - public float xAdvanceOffset; - } - - - [StructLayout(LayoutKind.Sequential)] - public struct FT_GlyphInfo - { - public int id; - public float x; - public float y; - public float width; - public float height; - public float xOffset; - public float yOffset; - public float xAdvance; - } - - - [StructLayout(LayoutKind.Sequential)] - public struct FT_FaceInfo - { - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] - public string name; - public int pointSize; - public int padding; - public float lineHeight; - public float baseline; - public float ascender; - public float descender; - public float centerLine; - public float underline; - public float underlineThickness; - public int characterCount; - public int atlasWidth; - public int atlasHeight; - } - */ -} diff --git a/Scripts/Editor/TMPro_TexturePostProcessor.cs b/Scripts/Editor/TMPro_TexturePostProcessor.cs index 51d6297..6574610 100644 --- a/Scripts/Editor/TMPro_TexturePostProcessor.cs +++ b/Scripts/Editor/TMPro_TexturePostProcessor.cs @@ -13,6 +13,8 @@ internal class TMPro_TexturePostProcessor : AssetPostprocessor { private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { + bool textureImported = false; + foreach (var asset in importedAssets) { // Return if imported asset path is outside of the project. @@ -28,19 +30,28 @@ private static void OnPostprocessAllAssets(string[] importedAssets, string[] del // Only refresh font asset definition if font asset was previously initialized. if (fontAsset != null && fontAsset.m_CharacterLookupDictionary != null) TMP_EditorResourceManager.RegisterFontAssetForDefinitionRefresh(fontAsset); + + continue; } if (assetType == typeof(Texture2D)) - { - Texture2D tex = AssetDatabase.LoadAssetAtPath(asset, typeof(Texture2D)) as Texture2D; + textureImported = true; + } + + // If textures were imported, issue callback to any potential text objects that might require updating. + if (textureImported) + TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, null); + } + } - if (tex == null) - continue; + internal class TMP_FontAssetPostProcessor : UnityEditor.AssetModificationProcessor + { + static AssetDeleteResult OnWillDeleteAsset(string path, RemoveAssetOptions opt) + { + if (AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(TMP_FontAsset)) + TMP_ResourceManager.RebuildFontAssetCache(); - TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, tex); - Resources.UnloadAsset(tex); - } - } + return AssetDeleteResult.DidNotDelete; } } } diff --git a/Scripts/Editor/Unity.TextMeshPro.Editor.asmdef b/Scripts/Editor/Unity.TextMeshPro.Editor.asmdef index 7e7adcf..795d056 100644 --- a/Scripts/Editor/Unity.TextMeshPro.Editor.asmdef +++ b/Scripts/Editor/Unity.TextMeshPro.Editor.asmdef @@ -1,7 +1,11 @@ { "name": "Unity.TextMeshPro.Editor", + "rootNamespace": "", "references": [ - "Unity.TextMeshPro" + "Unity.TextMeshPro", + "Unity.RenderPipelines.HighDefinition.Editor", + "Unity.RenderPipelines.Core.Runtime", + "Unity.RenderPipelines.HighDefinition.Runtime" ], "includePlatforms": [ "Editor" @@ -17,6 +21,11 @@ "name": "com.unity.textcore", "expression": "1.0.0-preview.0", "define": "TEXTCORE_1_0_OR_NEWER" + }, + { + "name": "com.unity.render-pipelines.high-definition", + "expression": "7.5.0", + "define": "HDRP_7_5_OR_NEWER" } ], "noEngineReferences": false diff --git a/Scripts/Runtime/FontFeatureCommonGPOS.cs b/Scripts/Runtime/FontFeatureCommonGPOS.cs new file mode 100644 index 0000000..5f60008 --- /dev/null +++ b/Scripts/Runtime/FontFeatureCommonGPOS.cs @@ -0,0 +1,151 @@ +using System; +using UnityEngine; + + +namespace TMPro +{ + /// + /// + /// + [Serializable] + public struct GlyphAnchorPoint + { + /// + /// The x coordinate of the anchor point relative to the glyph. + /// + public float xCoordinate { get { return m_XCoordinate; } set { m_XCoordinate = value; } } + + /// + /// The y coordinate of the anchor point relative to the glyph. + /// + public float yCoordinate { get { return m_YCoordinate; } set { m_YCoordinate = value; } } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + private float m_XCoordinate; + + [SerializeField] + private float m_YCoordinate; + } + + /// + /// + /// + [Serializable] + public struct MarkPositionAdjustment + { + /// + /// The x coordinate of the anchor point relative to the glyph. + /// + public float xPositionAdjustment { get { return m_XPositionAdjustment; } set { m_XPositionAdjustment = value; } } + + /// + /// The y coordinate of the anchor point relative to the glyph. + /// + public float yPositionAdjustment { get { return m_YPositionAdjustment; } set { m_YPositionAdjustment = value; } } + + + public MarkPositionAdjustment(float x, float y) + { + m_XPositionAdjustment = x; + m_YPositionAdjustment = y; + } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + private float m_XPositionAdjustment; + + [SerializeField] + private float m_YPositionAdjustment; + }; + + + /// + /// + /// + [Serializable] + public struct MarkToBaseAdjustmentRecord + { + /// + /// The index of the base glyph + /// + public uint baseGlyphID { get { return m_BaseGlyphID; } set { m_BaseGlyphID = value; } } + + /// + /// + /// + public GlyphAnchorPoint baseGlyphAnchorPoint { get { return m_BaseGlyphAnchorPoint; } set { m_BaseGlyphAnchorPoint = value; } } + + /// + /// The index of the mark glyph + /// + public uint markGlyphID { get { return m_MarkGlyphID; } set { m_MarkGlyphID = value; } } + + /// + /// + /// + public MarkPositionAdjustment markPositionAdjustment { get { return m_MarkPositionAdjustment; } set { m_MarkPositionAdjustment = value; } } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + private uint m_BaseGlyphID; + + [SerializeField] + private GlyphAnchorPoint m_BaseGlyphAnchorPoint; + + [SerializeField] + private uint m_MarkGlyphID; + + [SerializeField] + private MarkPositionAdjustment m_MarkPositionAdjustment; + } + + [Serializable] + public struct MarkToMarkAdjustmentRecord + { + /// + /// The index of the base glyph + /// + public uint baseMarkGlyphID { get { return m_BaseMarkGlyphID; } set { m_BaseMarkGlyphID = value; } } + + /// + /// + /// + public GlyphAnchorPoint baseMarkGlyphAnchorPoint { get { return m_BaseMarkGlyphAnchorPoint; } set { m_BaseMarkGlyphAnchorPoint = value; } } + + /// + /// The index of the mark glyph + /// + public uint combiningMarkGlyphID { get { return m_CombiningMarkGlyphID; } set { m_CombiningMarkGlyphID = value; } } + + /// + /// + /// + public MarkPositionAdjustment combiningMarkPositionAdjustment { get { return m_CombiningMarkPositionAdjustment; } set { m_CombiningMarkPositionAdjustment = value; } } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + private uint m_BaseMarkGlyphID; + + [SerializeField] + private GlyphAnchorPoint m_BaseMarkGlyphAnchorPoint; + + [SerializeField] + private uint m_CombiningMarkGlyphID; + + [SerializeField] + private MarkPositionAdjustment m_CombiningMarkPositionAdjustment; + } +} diff --git a/Scripts/Runtime/FontFeatureCommonGPOS.cs.meta b/Scripts/Runtime/FontFeatureCommonGPOS.cs.meta new file mode 100644 index 0000000..c64f0ec --- /dev/null +++ b/Scripts/Runtime/FontFeatureCommonGPOS.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a4bbbb1fed4e2b7448af9313d6d00536 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Runtime/FontFeatureCommonGSUB.cs b/Scripts/Runtime/FontFeatureCommonGSUB.cs new file mode 100644 index 0000000..135472f --- /dev/null +++ b/Scripts/Runtime/FontFeatureCommonGSUB.cs @@ -0,0 +1,76 @@ +using System; +using UnityEngine; + + +namespace TMPro +{ + /// + /// + /// + [Serializable] + public struct SingleSubstitutionRecord + { + + } + + /// + /// + /// + [Serializable] + public struct MultipleSubstitutionRecord + { + /// + /// + /// + public uint targetGlyphID { get { return m_TargetGlyphID; } set { m_TargetGlyphID = value; } } + + public uint[] substituteGlyphIDs { get { return m_SubstituteGlyphIDs; } set { m_SubstituteGlyphIDs = value; } } + + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + private uint m_TargetGlyphID; + + [SerializeField] + private uint[] m_SubstituteGlyphIDs; + } + + /// + /// + /// + [Serializable] + public struct AlternateSubstitutionRecord + { + + } + + /// + /// + /// + [Serializable] + public struct LigatureSubstitutionRecord + { + /// + /// + /// + public uint[] componentGlyphIDs { get { return m_ComponentGlyphIDs; } set { m_ComponentGlyphIDs = value; } } + + /// + /// + /// + public uint ligatureGlyphID { get { return m_LigatureGlyphID; } set { m_LigatureGlyphID = value; } } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + private uint[] m_ComponentGlyphIDs; + + [SerializeField] + private uint m_LigatureGlyphID; + } +} diff --git a/Scripts/Runtime/FontFeatureCommonGSUB.cs.meta b/Scripts/Runtime/FontFeatureCommonGSUB.cs.meta new file mode 100644 index 0000000..adbd686 --- /dev/null +++ b/Scripts/Runtime/FontFeatureCommonGSUB.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 77b2d47bca9376f4a8ae69084a485957 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Runtime/MaterialReferenceManager.cs b/Scripts/Runtime/MaterialReferenceManager.cs index 966e3af..377502f 100644 --- a/Scripts/Runtime/MaterialReferenceManager.cs +++ b/Scripts/Runtime/MaterialReferenceManager.cs @@ -110,7 +110,8 @@ private void AddSpriteAssetInternal(int hashCode, TMP_SpriteAsset spriteAsset) m_FontMaterialReferenceLookup.Add(hashCode, spriteAsset.material); // Compatibility check - if (spriteAsset.hashCode == 0) spriteAsset.hashCode = hashCode; + if (spriteAsset.hashCode == 0) + spriteAsset.hashCode = hashCode; } @@ -522,7 +523,7 @@ public MaterialReference(int index, TMP_FontAsset fontAsset, TMP_SpriteAsset spr this.fontAsset = fontAsset; this.spriteAsset = spriteAsset; this.material = material; - this.isDefaultMaterial = material.GetInstanceID() == fontAsset.material.GetInstanceID() ? true : false; + this.isDefaultMaterial = material.GetInstanceID() == fontAsset.material.GetInstanceID(); this.isFallbackMaterial = false; this.fallbackMaterial = null; this.padding = padding; @@ -578,12 +579,10 @@ public static int AddMaterialReference(Material material, TMP_FontAsset fontAsse materialReferences[index].fontAsset = fontAsset; materialReferences[index].spriteAsset = null; materialReferences[index].material = material; - materialReferences[index].isDefaultMaterial = materialID == fontAsset.material.GetInstanceID() ? true : false; - //materialReferences[index].padding = 0; + materialReferences[index].isDefaultMaterial = materialID == fontAsset.material.GetInstanceID(); materialReferences[index].referenceCount = 0; return index; - } @@ -616,7 +615,6 @@ public static int AddMaterialReference(Material material, TMP_SpriteAsset sprite materialReferences[index].spriteAsset = spriteAsset; materialReferences[index].material = material; materialReferences[index].isDefaultMaterial = true; - //materialReferences[index].padding = 0; materialReferences[index].referenceCount = 0; return index; diff --git a/Scripts/Runtime/TMP_Asset.cs b/Scripts/Runtime/TMP_Asset.cs index 60ed878..0d586a6 100644 --- a/Scripts/Runtime/TMP_Asset.cs +++ b/Scripts/Runtime/TMP_Asset.cs @@ -1,13 +1,23 @@ using System; using UnityEngine; +using UnityEngine.Serialization; namespace TMPro { - // Base class inherited by the various TextMeshPro Assets. [Serializable] public abstract class TMP_Asset : ScriptableObject { + /// + /// The version of the text asset class. + /// Version 1.1.0 introduces new data structure to be compatible with new font asset structure. + /// + public string version + { + get { return m_Version; } + internal set { m_Version = value; } + } + /// /// Instance ID of the TMP Asset /// @@ -21,22 +31,65 @@ public int instanceID return m_InstanceID; } } - private int m_InstanceID; /// /// HashCode based on the name of the asset. /// - public int hashCode; + public int hashCode + { + get + { + if (m_HashCode == 0) + m_HashCode = TMP_TextUtilities.GetHashCode(name); + + return m_HashCode; + } + set => m_HashCode = value; + } /// /// The material used by this asset. /// - public Material material; + public Material material + { + get => m_Material; + set => m_Material = value; + } /// /// HashCode based on the name of the material assigned to this asset. /// - public int materialHashCode; + public int materialHashCode + { + get + { + if (m_MaterialHashCode == 0) + { + if (m_Material == null) + return 0; + + m_MaterialHashCode = TMP_TextUtilities.GetSimpleHashCode(m_Material.name); + } + + return m_MaterialHashCode; + } + set => m_MaterialHashCode = value; + } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + internal string m_Version; + + internal int m_InstanceID; + + internal int m_HashCode; + + [SerializeField][FormerlySerializedAs("material")] + internal Material m_Material; + internal int m_MaterialHashCode; } } diff --git a/Scripts/Runtime/TMP_CharacterInfo.cs b/Scripts/Runtime/TMP_CharacterInfo.cs index 2a919f6..779b7bd 100644 --- a/Scripts/Runtime/TMP_CharacterInfo.cs +++ b/Scripts/Runtime/TMP_CharacterInfo.cs @@ -7,9 +7,9 @@ namespace TMPro public struct TMP_Vertex { public Vector3 position; - public Vector2 uv; + public Vector4 uv; public Vector2 uv2; - public Vector2 uv4; + //public Vector2 uv4; public Color32 color; public static TMP_Vertex zero { get { return k_Zero; } } @@ -203,6 +203,7 @@ public struct TMP_CharacterInfo public float descender; internal float adjustedAscender; internal float adjustedDescender; + internal float adjustedHorizontalAdvance; public float aspectRatio; public float scale; diff --git a/Scripts/Runtime/TMP_DefaultControls.cs b/Scripts/Runtime/TMP_DefaultControls.cs index 7d770a8..9f9e435 100644 --- a/Scripts/Runtime/TMP_DefaultControls.cs +++ b/Scripts/Runtime/TMP_DefaultControls.cs @@ -182,9 +182,8 @@ public static GameObject CreateInputField(Resources resources) TMP_InputField inputField = root.AddComponent(); SetDefaultColorTransitionValues(inputField); - // Use UI.Mask for Unity 5.0 - 5.1 and 2D RectMask for Unity 5.2 and up RectMask2D rectMask = textArea.AddComponent(); - #if UNITY_2019_4_OR_NEWER && !UNITY_2019_4_1 && !UNITY_2019_4_2 && !UNITY_2019_4_3 && !UNITY_2019_4_4 && !UNITY_2019_4_5 && !UNITY_2019_4_6 && !UNITY_2019_4_7 && !UNITY_2019_4_8 && !UNITY_2019_4_9 && !UNITY_2019_4_10 && !UNITY_2019_4_11 + #if UNITY_2019_4_OR_NEWER rectMask.padding = new Vector4(-8, -5, -8, -5); #endif @@ -198,7 +197,7 @@ public static GameObject CreateInputField(Resources resources) TextMeshProUGUI text = childText.AddComponent(); text.text = ""; - text.enableWordWrapping = false; + text.textWrappingMode = TextWrappingModes.NoWrap; text.extraPadding = true; text.richText = true; SetDefaultTextValues(text); @@ -207,7 +206,7 @@ public static GameObject CreateInputField(Resources resources) placeholder.text = "Enter text..."; placeholder.fontSize = 14; placeholder.fontStyle = FontStyles.Italic; - placeholder.enableWordWrapping = false; + placeholder.textWrappingMode = TextWrappingModes.NoWrap; placeholder.extraPadding = true; // Make placeholder color half as opaque as normal text color. diff --git a/Scripts/Runtime/TMP_Dropdown.cs b/Scripts/Runtime/TMP_Dropdown.cs index cb2d9f9..29bd625 100644 --- a/Scripts/Runtime/TMP_Dropdown.cs +++ b/Scripts/Runtime/TMP_Dropdown.cs @@ -60,6 +60,8 @@ public class OptionData private string m_Text; [SerializeField] private Sprite m_Image; + [SerializeField] + private Color m_Color = Color.white; /// /// The text associated with the option. @@ -71,6 +73,11 @@ public class OptionData /// public Sprite image { get { return m_Image; } set { m_Image = value; } } + /// + /// The color associated with the option. + /// + public Color color { get { return m_Color; } set { m_Color = value; } } + public OptionData() { } public OptionData(string text) @@ -88,10 +95,12 @@ public OptionData(Sprite image) /// /// Optional text for the option. /// Optional image for the option. - public OptionData(string text, Sprite image) + /// Optional color for the option. + public OptionData(string text, Sprite image, Color color) { this.text = text; this.image = image; + this.color = color; } } @@ -125,6 +134,10 @@ public OptionDataList() /// public class DropdownEvent : UnityEvent { } + static readonly OptionData k_NothingOption = new OptionData { text = "Nothing" }; + static readonly OptionData k_EverythingOption = new OptionData { text = "Everything" }; + static readonly OptionData k_MixedOption = new OptionData { text = "Mixed..." }; + // Template used to create the dropdown. [SerializeField] private RectTransform m_Template; @@ -182,6 +195,9 @@ public class DropdownEvent : UnityEvent { } [SerializeField] private int m_Value; + [SerializeField] + private bool m_MultiSelect; + [Space] // Items that will be visible when the dropdown is shown. @@ -421,7 +437,11 @@ void SetValue(int value, bool sendCallback = true) if (Application.isPlaying && (value == m_Value || options.Count == 0)) return; - m_Value = Mathf.Clamp(value, m_Placeholder ? -1 : 0, options.Count - 1); + if (m_MultiSelect) + m_Value = value; + else + m_Value = Mathf.Clamp(value, m_Placeholder ? -1 : 0, options.Count - 1); + RefreshShownValue(); if (sendCallback) @@ -434,6 +454,8 @@ void SetValue(int value, bool sendCallback = true) public bool IsExpanded { get { return m_Dropdown != null; } } + public bool MultiSelect { get { return m_MultiSelect; } set { m_MultiSelect = value; } } + protected TMP_Dropdown() { } protected override void Awake() @@ -444,7 +466,7 @@ protected override void Awake() #endif if (m_CaptionImage) - m_CaptionImage.enabled = (m_CaptionImage.sprite != null); + m_CaptionImage.enabled = (m_CaptionImage.sprite != null && m_CaptionImage.color.a > 0); if (m_Template) m_Template.gameObject.SetActive(false); @@ -494,8 +516,25 @@ public void RefreshShownValue() { OptionData data = s_NoOptionData; - if (options.Count > 0 && m_Value >= 0) - data = options[Mathf.Clamp(m_Value, 0, options.Count - 1)]; + if (options.Count > 0) + { + if (m_MultiSelect) + { + int firstActiveFlag = FirstActiveFlagIndex(m_Value); + if (m_Value == 0 || firstActiveFlag >= options.Count) + data = k_NothingOption; + else if (IsEverythingValue(options.Count, m_Value)) + data = k_EverythingOption; + else if (Mathf.IsPowerOfTwo(m_Value) && m_Value > 0) + data = options[firstActiveFlag]; + else + data = k_MixedOption; + } + else if (m_Value >= 0) + { + data = options[Mathf.Clamp(m_Value, 0, options.Count - 1)]; + } + } if (m_CaptionText) { @@ -507,11 +546,9 @@ public void RefreshShownValue() if (m_CaptionImage) { - if (data != null) - m_CaptionImage.sprite = data.image; - else - m_CaptionImage.sprite = null; - m_CaptionImage.enabled = (m_CaptionImage.sprite != null); + m_CaptionImage.sprite = data.image; + m_CaptionImage.color = data.color; + m_CaptionImage.enabled = (m_CaptionImage.sprite != null && m_CaptionImage.color.a > 0); } if (m_Placeholder) @@ -808,6 +845,44 @@ public void Show() m_Items.Clear(); Toggle prev = null; + if (m_MultiSelect && options.Count > 0) + { + DropdownItem item = AddItem(k_NothingOption, value == 0, itemTemplate, m_Items); + if (item.image != null) + item.image.gameObject.SetActive(false); + + Toggle nothingToggle = item.toggle; + nothingToggle.isOn = value == 0; + nothingToggle.onValueChanged.AddListener(x => OnSelectItem(nothingToggle)); + prev = nothingToggle; + + bool isEverythingValue = IsEverythingValue(options.Count, value); + item = AddItem(k_EverythingOption, isEverythingValue, itemTemplate, m_Items); + if (item.image != null) + item.image.gameObject.SetActive(false); + + Toggle everythingToggle = item.toggle; + everythingToggle.isOn = isEverythingValue; + everythingToggle.onValueChanged.AddListener(x => OnSelectItem(everythingToggle)); + + // Automatically set up explicit navigation + if (prev != null) + { + Navigation prevNav = prev.navigation; + Navigation toggleNav = item.toggle.navigation; + prevNav.mode = Navigation.Mode.Explicit; + toggleNav.mode = Navigation.Mode.Explicit; + + prevNav.selectOnDown = item.toggle; + prevNav.selectOnRight = item.toggle; + toggleNav.selectOnLeft = prev; + toggleNav.selectOnUp = prev; + + prev.navigation = prevNav; + item.toggle.navigation = toggleNav; + } + } + for (int i = 0; i < options.Count; ++i) { OptionData data = options[i]; @@ -816,7 +891,11 @@ public void Show() continue; // Automatically set up a toggle state change listener - item.toggle.isOn = value == i; + if (m_MultiSelect) + item.toggle.isOn = (value & (1 << i)) != 0; + else + item.toggle.isOn = value == i; + item.toggle.onValueChanged.AddListener(x => OnSelectItem(item.toggle)); // Select current option @@ -895,6 +974,29 @@ public void Show() m_Blocker = CreateBlocker(rootCanvas); } + static bool IsEverythingValue(int count, int value) + { + var result = true; + for (var i = 0; i < count; i++) + { + if ((value & 1 << i) == 0) + result = false; + } + + return result; + } + + static int EverythingValue(int count) + { + int result = 0; + for (var i = 0; i < count; i++) + { + result |= 1 << i; + } + + return result; + } + /// /// Create a blocker that blocks clicks to other controls while the dropdown list is open. /// @@ -1044,10 +1146,12 @@ private DropdownItem AddItem(OptionData data, bool selected, DropdownItem itemTe // Set the item's data if (item.text) item.text.text = data.text; + if (item.image) { item.image.sprite = data.image; - item.image.enabled = (item.image.sprite != null); + item.image.color = data.color; + item.image.enabled = (item.image.sprite != null && data.color.a > 0); } items.Add(item); @@ -1133,13 +1237,10 @@ private void ImmediateDestroyDropdownList() // Change the value and hide the dropdown. private void OnSelectItem(Toggle toggle) { - if (!toggle.isOn) - toggle.isOn = true; - int selectedIndex = -1; Transform tr = toggle.transform; Transform parent = tr.parent; - for (int i = 0; i < parent.childCount; i++) + for (int i = 1; i < parent.childCount; i++) { if (parent.GetChild(i) == tr) { @@ -1152,8 +1253,65 @@ private void OnSelectItem(Toggle toggle) if (selectedIndex < 0) return; - value = selectedIndex; + if (m_MultiSelect) + { + switch (selectedIndex) + { + case 0: // Nothing + value = 0; + for (var i = 3; i < parent.childCount; i++) + { + var toggleComponent = parent.GetChild(i).GetComponentInChildren(); + if (toggleComponent) + toggleComponent.SetIsOnWithoutNotify(false); + } + + toggle.isOn = true; + break; + case 1: // Everything + value = EverythingValue(options.Count); + for (var i = 3; i < parent.childCount; i++) + { + var toggleComponent = parent.GetChild(i).GetComponentInChildren(); + if (toggleComponent) + toggleComponent.SetIsOnWithoutNotify(i > 2); + } + break; + default: + var flagValue = 1 << (selectedIndex - 2); + var wasSelected = (value & flagValue) != 0; + toggle.SetIsOnWithoutNotify(!wasSelected); + + if (wasSelected) + value &= ~flagValue; + else + value |= flagValue; + + break; + } + } + else + { + if (!toggle.isOn) + toggle.SetIsOnWithoutNotify(true); + + value = selectedIndex; + } + Hide(); } + + static int FirstActiveFlagIndex(int value) + { + if (value == 0) + return 0; + + const int bits = sizeof(int) * 8; + for (var i = 0; i < bits; i++) + if ((value & 1 << i) != 0) + return i; + + return 0; + } } } diff --git a/Scripts/Runtime/TMP_DynamicFontAssetUtilities.cs b/Scripts/Runtime/TMP_DynamicFontAssetUtilities.cs new file mode 100644 index 0000000..e2d07cf --- /dev/null +++ b/Scripts/Runtime/TMP_DynamicFontAssetUtilities.cs @@ -0,0 +1,188 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.TextCore.LowLevel; + + +namespace TMPro +{ + internal class TMP_DynamicFontAssetUtilities + { + private static TMP_DynamicFontAssetUtilities s_Instance = new TMP_DynamicFontAssetUtilities(); + + private Dictionary s_SystemFontLookup; + private string[] s_SystemFontPaths; + private uint s_RegularStyleNameHashCode = 1291372090; + + public struct FontReference + { + public string familyName; + public string styleName; + public int faceIndex; + public string filePath; + public ulong hashCode; + + /// + /// Constructor for new FontReference + /// + /// String that combines the family name with style name + /// Index of the font face and style. + public FontReference(string fontFilePath, string faceNameAndStyle, int index) + { + familyName = null; + styleName = null; + faceIndex = index; + uint familyNameHashCode = 0; + uint styleNameHashCode = 0; + filePath = fontFilePath; + + int length = faceNameAndStyle.Length; + char[] conversionArray = new char[length]; + + int readingFlag = 0; + int writingIndex = 0; + + for (int i = 0; i < length; i++) + { + char c = faceNameAndStyle[i]; + + // Read family name + if (readingFlag == 0) + { + bool isSeparator = i + 2 < length && c == ' ' && faceNameAndStyle[i + 1] == '-' && faceNameAndStyle[i + 2] == ' '; + + if (isSeparator) + { + readingFlag = 1; + this.familyName = new string(conversionArray, 0, writingIndex); + i += 2; + writingIndex = 0; + continue; + } + + familyNameHashCode = (familyNameHashCode << 5) + familyNameHashCode ^ TMP_TextUtilities.ToUpperFast(c); + conversionArray[writingIndex++] = c; + continue; + } + + // Read style name + if (readingFlag == 1) + { + styleNameHashCode = (styleNameHashCode << 5) + styleNameHashCode ^ TMP_TextUtilities.ToUpperFast(c); + conversionArray[writingIndex++] = c; + + if (i + 1 == length) + this.styleName = new string(conversionArray, 0, writingIndex); + } + } + + hashCode = (ulong)styleNameHashCode << 32 | familyNameHashCode; + } + } + + + void InitializeSystemFontReferenceCache() + { + if (s_SystemFontLookup == null) + s_SystemFontLookup = new Dictionary(); + else + s_SystemFontLookup.Clear(); + + if (s_SystemFontPaths == null) + s_SystemFontPaths = Font.GetPathsToOSFonts(); + + for (int i = 0; i < s_SystemFontPaths.Length; i++) + { + // Load font at the given path + FontEngineError error = FontEngine.LoadFontFace(s_SystemFontPaths[i]); + if (error != FontEngineError.Success) + { + Debug.LogWarning("Error [" + error + "] trying to load the font at path [" + s_SystemFontPaths[i] + "]."); + continue; + } + + // Get font faces and styles for this font + string[] fontFaces = FontEngine.GetFontFaces(); + + // Iterate over each font face + for (int j = 0; j < fontFaces.Length; j++) + { + FontReference fontRef = new FontReference(s_SystemFontPaths[i], fontFaces[j], j); + + if (s_SystemFontLookup.ContainsKey(fontRef.hashCode)) + { + //Debug.Log("[" + i + "] Family Name [" + fontRef.familyName + "] Style Name [" + fontRef.styleName + "] Index [" + fontRef.faceIndex + "] HashCode [" + fontRef.hashCode + "] Path [" + fontRef.filePath + "]."); + continue; + } + + // Add font reference to lookup dictionary + s_SystemFontLookup.Add(fontRef.hashCode, fontRef); + + Debug.Log("[" + i + "] Family Name [" + fontRef.familyName + "] Style Name [" + fontRef.styleName + "] Index [" + fontRef.faceIndex + "] HashCode [" + fontRef.hashCode + "] Path [" + fontRef.filePath + "]."); + } + + // Unload current font face. + FontEngine.UnloadFontFace(); + } + } + + + /// + /// + /// + /// + /// + /// + public static bool TryGetSystemFontReference(string familyName, out FontReference fontRef) + { + return s_Instance.TryGetSystemFontReferenceInternal(familyName, null, out fontRef); + } + + /// + /// + /// + /// + /// + /// + /// + public static bool TryGetSystemFontReference(string familyName, string styleName, out FontReference fontRef) + { + return s_Instance.TryGetSystemFontReferenceInternal(familyName, styleName, out fontRef); + } + + bool TryGetSystemFontReferenceInternal(string familyName, string styleName, out FontReference fontRef) + { + if (s_SystemFontLookup == null) + InitializeSystemFontReferenceCache(); + + fontRef = new FontReference(); + + // Compute family name hash code + uint familyNameHashCode = TMP_TextUtilities.GetHashCodeCaseInSensitive(familyName); + uint styleNameHashCode = string.IsNullOrEmpty(styleName) ? s_RegularStyleNameHashCode : TMP_TextUtilities.GetHashCodeCaseInSensitive(styleName); + ulong key = (ulong)styleNameHashCode << 32 | familyNameHashCode; + + // Lookup font reference + if (s_SystemFontLookup.ContainsKey(key)) + { + fontRef = s_SystemFontLookup[key]; + return true; + } + + // Return if specified family and style name is not found. + if (styleNameHashCode != s_RegularStyleNameHashCode) + return false; + + // Return first potential reference for the given family name + foreach (KeyValuePair pair in s_SystemFontLookup) + { + if (pair.Value.familyName == familyName) + { + fontRef = pair.Value; + return true; + } + } + + return false; + } + } +} diff --git a/Scripts/Runtime/TMP_DynamicFontAssetUtilities.cs.meta b/Scripts/Runtime/TMP_DynamicFontAssetUtilities.cs.meta new file mode 100644 index 0000000..1e38fc5 --- /dev/null +++ b/Scripts/Runtime/TMP_DynamicFontAssetUtilities.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aaf9006568d76504f982305b517fb490 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Runtime/TMP_FontAsset.cs b/Scripts/Runtime/TMP_FontAsset.cs index 243dc38..07fc1ab 100644 --- a/Scripts/Runtime/TMP_FontAsset.cs +++ b/Scripts/Runtime/TMP_FontAsset.cs @@ -1,41 +1,29 @@ using System; +using System.Collections.Generic; +using System.Linq; using UnityEngine; using UnityEngine.Serialization; using UnityEngine.TextCore; using UnityEngine.TextCore.LowLevel; using Unity.Profiling; -using System.Collections.Generic; -using System.Linq; - -#if UNITY_EDITOR && UNITY_2018_4_OR_NEWER && !UNITY_2018_4_0 && !UNITY_2018_4_1 && !UNITY_2018_4_2 && !UNITY_2018_4_3 && !UNITY_2018_4_4 - using UnityEditor.TextCore.LowLevel; -#endif namespace TMPro { + /// + /// Atlas population modes which ultimately determines the type of font asset. + /// public enum AtlasPopulationMode { Static = 0x0, Dynamic = 0x1, + DynamicOS = 0x2 } [Serializable][ExcludeFromPresetAttribute] public class TMP_FontAsset : TMP_Asset { - /// - /// The version of the font asset class. - /// Version 1.1.0 adds support for the new TextCore.FontEngine and Dynamic SDF system. - /// - public string version - { - get { return m_Version; } - internal set { m_Version = value; } - } - [SerializeField] - private string m_Version; - /// /// This field is set when the font asset is first created. /// @@ -46,7 +34,27 @@ public string version /// /// Persistent reference to the source font file maintained in the editor. /// - [SerializeField] + internal Font SourceFont_EditorRef + { + get + { + if (m_SourceFontFile_EditorRef == null) + m_SourceFontFile_EditorRef = GetSourceFontRef?.Invoke(m_SourceFontFileGUID); + + return m_SourceFontFile_EditorRef; + } + + set + { + m_SourceFontFile_EditorRef = value; + m_SourceFontFileGUID = SetSourceFontGUID?.Invoke(m_SourceFontFile_EditorRef); + + if (m_AtlasPopulationMode == AtlasPopulationMode.Static || m_AtlasPopulationMode == AtlasPopulationMode.DynamicOS) + m_SourceFontFile = null; + else + m_SourceFontFile = m_SourceFontFile_EditorRef; + } + } internal Font m_SourceFontFile_EditorRef; #endif @@ -70,7 +78,7 @@ public AtlasPopulationMode atlasPopulationMode m_AtlasPopulationMode = value; #if UNITY_EDITOR - if (m_AtlasPopulationMode == AtlasPopulationMode.Static) + if (m_AtlasPopulationMode == AtlasPopulationMode.Static || m_AtlasPopulationMode == AtlasPopulationMode.DynamicOS) m_SourceFontFile = null; else if (m_AtlasPopulationMode == AtlasPopulationMode.Dynamic) m_SourceFontFile = m_SourceFontFile_EditorRef; @@ -80,6 +88,11 @@ public AtlasPopulationMode atlasPopulationMode [SerializeField] private AtlasPopulationMode m_AtlasPopulationMode; + /// + /// Field used to identify dynamic OS font assets used internally. + /// + [SerializeField] + internal bool InternalDynamicOS; /// /// Information about the font's face. @@ -92,6 +105,37 @@ public FaceInfo faceInfo [SerializeField] internal FaceInfo m_FaceInfo; + /// + /// + /// + internal int familyNameHashCode + { + get + { + if (m_FamilyNameHashCode == 0) + m_FamilyNameHashCode = TMP_TextUtilities.GetHashCode(m_FaceInfo.familyName); + + return m_FamilyNameHashCode; + } + set => m_FamilyNameHashCode = value; + } + private int m_FamilyNameHashCode; + + /// + /// + /// + internal int styleNameHashCode + { + get + { + if (m_StyleNameHashCode == 0) + m_StyleNameHashCode = TMP_TextUtilities.GetHashCode(m_FaceInfo.styleName); + + return m_StyleNameHashCode; + } + set => m_StyleNameHashCode = value; + } + private int m_StyleNameHashCode; /// /// List of glyphs contained in the font asset. @@ -415,6 +459,16 @@ public TMP_FontWeightPair[] fontWeightTable internal bool IsFontAssetLookupTablesDirty; + // Editor Only Callbacks + #if UNITY_EDITOR + internal static Action OnFontAssetTextureChanged; + internal static Action RegisterResourceForUpdate; + internal static Action RegisterResourceForReimport; + internal static Action SetAtlasTextureIsReadable; + internal static Func GetSourceFontRef; + internal static Func SetSourceFontGUID; + #endif + // Profiler Marker declarations private static ProfilerMarker k_ReadFontAssetDefinitionMarker = new ProfilerMarker("TMP.ReadFontAssetDefinition"); private static ProfilerMarker k_AddSynthesizedCharactersMarker = new ProfilerMarker("TMP.AddSynthesizedCharacters"); @@ -424,60 +478,100 @@ public TMP_FontWeightPair[] fontWeightTable private static ProfilerMarker k_ClearFontAssetDataMarker = new ProfilerMarker("TMP.ClearFontAssetData"); private static ProfilerMarker k_UpdateFontAssetDataMarker = new ProfilerMarker("TMP.UpdateFontAssetData"); + // ================================================================================ + // Functions used to create font asset at runtime + // ================================================================================ + /// - /// Create new instance of a font asset using default settings. + /// Creates a new font asset instance from the given family name and style. /// - /// - /// + /// The family name of the source font. + /// The style name of the source font face. + /// Optional point size. + /// An instance of the newly created font asset. + #if UNITY_2020_3_OR_NEWER && !(UNITY_2020_3_1 || UNITY_2020_3_2 || UNITY_2020_3_3 || UNITY_2020_3_4 || UNITY_2020_3_5 || UNITY_2020_3_6 || UNITY_2021_1_1|| UNITY_2021_1_2|| UNITY_2021_1_3|| UNITY_2021_1_4) + public static TMP_FontAsset CreateFontAsset(string familyName, string styleName, int pointSize = 90) + { + if (FontEngine.TryGetSystemFontReference(familyName, styleName, out FontReference fontRef)) + return CreateFontAsset(fontRef.filePath, fontRef.faceIndex, pointSize, 9, GlyphRenderMode.SDFAA, 1024, 1024); + + Debug.Log("Unable to find a font file with the specified Family Name [" + familyName + "] and Style [" + styleName + "]."); + + return null; + } + #endif + + static TMP_FontAsset CreateFontAsset(string fontFilePath, int faceIndex, int samplingPointSize, int atlasPadding, GlyphRenderMode renderMode, int atlasWidth, int atlasHeight, AtlasPopulationMode atlasPopulationMode = AtlasPopulationMode.DynamicOS, bool enableMultiAtlasSupport = true) + { + // Load Font Face + if (FontEngine.LoadFontFace(fontFilePath, samplingPointSize, faceIndex) != FontEngineError.Success) + { + Debug.Log("Unable to load font face for [" + fontFilePath + "]."); + return null; + } + + return CreateFontAssetInstance(null, atlasPadding, renderMode, atlasWidth, atlasHeight, atlasPopulationMode, enableMultiAtlasSupport); + } + + /// + /// Creates a new font asset instance from the provided font object. + /// + /// The source font object. + /// An instance of the newly created font asset. public static TMP_FontAsset CreateFontAsset(Font font) { - return CreateFontAsset(font, 90, 9, GlyphRenderMode.SDFAA, 1024, 1024, AtlasPopulationMode.Dynamic); + return CreateFontAsset(font, 90, 9, GlyphRenderMode.SDFAA, 1024, 1024); } /// - /// Create new instance of a font asset. + /// Creates a new font asset instance from the provided font object. /// - /// The source font file. + /// The source font object. /// The sampling point size. - /// The padding / spread between individual glyphs in the font asset. - /// + /// The padding between individual glyphs in the font atlas texture. + /// The atlas render mode. /// The atlas texture width. /// The atlas texture height. - /// - /// + /// The atlas population mode. + /// Enable multi atlas texture. + /// An instance of the newly created font asset. public static TMP_FontAsset CreateFontAsset(Font font, int samplingPointSize, int atlasPadding, GlyphRenderMode renderMode, int atlasWidth, int atlasHeight, AtlasPopulationMode atlasPopulationMode = AtlasPopulationMode.Dynamic, bool enableMultiAtlasSupport = true) { - // Initialize FontEngine - FontEngine.InitializeFontEngine(); + return CreateFontAsset(font, 0, samplingPointSize, atlasPadding, renderMode, atlasWidth, atlasHeight, atlasPopulationMode, enableMultiAtlasSupport); + } + static TMP_FontAsset CreateFontAsset(Font font, int faceIndex, int samplingPointSize, int atlasPadding, GlyphRenderMode renderMode, int atlasWidth, int atlasHeight, AtlasPopulationMode atlasPopulationMode = AtlasPopulationMode.Dynamic, bool enableMultiAtlasSupport = true) + { // Load Font Face - if (FontEngine.LoadFontFace(font, samplingPointSize) != FontEngineError.Success) + if (FontEngine.LoadFontFace(font, samplingPointSize, faceIndex) != FontEngineError.Success) { Debug.LogWarning("Unable to load font face for [" + font.name + "]. Make sure \"Include Font Data\" is enabled in the Font Import Settings.", font); return null; } + return CreateFontAssetInstance(font, atlasPadding, renderMode, atlasWidth, atlasHeight, atlasPopulationMode, enableMultiAtlasSupport); + } + + static TMP_FontAsset CreateFontAssetInstance(Font font, int atlasPadding, GlyphRenderMode renderMode, int atlasWidth, int atlasHeight, AtlasPopulationMode atlasPopulationMode, bool enableMultiAtlasSupport) + { // Create new font asset - TMP_FontAsset fontAsset = ScriptableObject.CreateInstance(); + TMP_FontAsset fontAsset = CreateInstance(); fontAsset.m_Version = "1.1.0"; fontAsset.faceInfo = FontEngine.GetFaceInfo(); - // Set font reference and GUID if (atlasPopulationMode == AtlasPopulationMode.Dynamic) + { fontAsset.sourceFontFile = font; - // Set persistent reference to source font file in the Editor only. - #if UNITY_EDITOR - string guid; - long localID; - - UnityEditor.AssetDatabase.TryGetGUIDAndLocalFileIdentifier(font, out guid, out localID); - fontAsset.m_SourceFontFileGUID = guid; - fontAsset.m_SourceFontFile_EditorRef = font; - #endif + #if UNITY_EDITOR + fontAsset.m_SourceFontFileGUID = SetSourceFontGUID?.Invoke(font); + fontAsset.m_SourceFontFile_EditorRef = font; + #endif + } fontAsset.atlasPopulationMode = atlasPopulationMode; + fontAsset.clearDynamicDataOnBuild = TMP_Settings.clearDynamicDataOnBuild; fontAsset.atlasWidth = atlasWidth; fontAsset.atlasHeight = atlasHeight; @@ -529,9 +623,16 @@ public static TMP_FontAsset CreateFontAsset(Font font, int samplingPointSize, in fontAsset.material = tmp_material; } - fontAsset.freeGlyphRects = new List(8) { new GlyphRect(0, 0, atlasWidth - packingModifier, atlasHeight - packingModifier) }; + fontAsset.freeGlyphRects = new List(8) {new GlyphRect(0, 0, atlasWidth - packingModifier, atlasHeight - packingModifier)}; fontAsset.usedGlyphRects = new List(8); + // Set the name of the font asset resources for tracking in the profiler + #if UNITY_EDITOR + string fontName = fontAsset.faceInfo.familyName + " - " + fontAsset.faceInfo.styleName; + fontAsset.material.name = fontName + " Material"; + fontAsset.atlasTextures[0].name = fontName + " Atlas"; + #endif + // TODO: Consider adding support for extracting glyph positioning data fontAsset.ReadFontAssetDefinition(); @@ -539,6 +640,9 @@ public static TMP_FontAsset CreateFontAsset(Font font, int samplingPointSize, in return fontAsset; } + // ================================================================================ + // + // ================================================================================ void Awake() { @@ -547,6 +651,13 @@ void Awake() UpgradeFontAsset(); } + private void OnDestroy() + { + DestroyAtlasTextures(); + + DestroyImmediate(m_Material); + } + #if UNITY_EDITOR private void OnValidate() { @@ -558,6 +669,9 @@ private void OnValidate() private static string s_DefaultMaterialSuffix = " Atlas Material"; + /// + /// Reads the various data tables of the font asset and populates various data structures to allow for faster lookup of related font asset data. + /// public void ReadFontAssetDefinition() { k_ReadFontAssetDefinitionMarker.Begin(); @@ -574,6 +688,20 @@ public void ReadFontAssetDefinition() // Add synthesized characters and adjust face metrics AddSynthesizedCharactersAndFaceMetrics(); + // Set Cap Line using the capital letter 'X' + if (m_FaceInfo.capLine == 0 && m_CharacterLookupDictionary.ContainsKey('X')) + { + uint glyphIndex = m_CharacterLookupDictionary['X'].glyphIndex; + m_FaceInfo.capLine = m_GlyphLookupDictionary[glyphIndex].metrics.horizontalBearingY; + } + + // Set Mean Line using the lowercase letter 'x' + if (m_FaceInfo.meanLine == 0 && m_CharacterLookupDictionary.ContainsKey('x')) + { + uint glyphIndex = m_CharacterLookupDictionary['x'].glyphIndex; + m_FaceInfo.meanLine = m_GlyphLookupDictionary[glyphIndex].metrics.horizontalBearingY; + } + // Adjust Font Scale for compatibility reasons if (m_FaceInfo.scale == 0) m_FaceInfo.scale = 1.0f; @@ -589,14 +717,14 @@ public void ReadFontAssetDefinition() m_AtlasPadding = (int)material.GetFloat(ShaderUtilities.ID_GradientScale) - 1; } - // Compute Hashcode for the font asset name - hashCode = TMP_TextUtilities.GetSimpleHashCode(this.name); - - // Compute Hashcode for the material name + // Compute hash codes for various properties of the font asset used for lookup. + hashCode = TMP_TextUtilities.GetHashCode(this.name); + familyNameHashCode = TMP_TextUtilities.GetHashCode(m_FaceInfo.familyName); + styleNameHashCode = TMP_TextUtilities.GetHashCode(m_FaceInfo.styleName); materialHashCode = TMP_TextUtilities.GetSimpleHashCode(this.name + s_DefaultMaterialSuffix); // Add reference to font asset in TMP Resource Manager - //TMP_ResourceManager.AddFontAsset(this); + TMP_ResourceManager.AddFontAsset(this); IsFontAssetLookupTablesDirty = false; @@ -615,8 +743,17 @@ internal void InitializeDictionaryLookupTables() // Initialize and populate character lookup dictionary InitializeCharacterLookupDictionary(); - // Initialize and populate character lookup dictionary + // + InitializeLigatureSubstitutionLookupDictionary(); + + // Initialize and populate glyph pair adjustment records InitializeGlyphPaidAdjustmentRecordsLookupDictionary(); + + // Initialize and populate mark to base adjustment records + InitializeMarkToBaseAdjustmentRecordsLookupDictionary(); + + // Initialize and populate mark to base adjustment records + InitializeMarkToMarkAdjustmentRecordsLookupDictionary(); } internal void InitializeGlyphLookupDictionary() @@ -684,10 +821,34 @@ internal void InitializeCharacterLookupDictionary() } // Clear internal fallback references - if (FallbackSearchQueryLookup == null) - FallbackSearchQueryLookup = new HashSet(); + //if (FallbackSearchQueryLookup == null) + // FallbackSearchQueryLookup = new HashSet(); + //else + // FallbackSearchQueryLookup.Clear(); + } + + internal void InitializeLigatureSubstitutionLookupDictionary() + { + if (m_FontFeatureTable.m_LigatureSubstitutionRecordLookup == null) + m_FontFeatureTable.m_LigatureSubstitutionRecordLookup = new Dictionary>(); else - FallbackSearchQueryLookup.Clear(); + m_FontFeatureTable.m_LigatureSubstitutionRecordLookup.Clear(); + + List substitutionRecords = m_FontFeatureTable.m_LigatureSubstitutionRecords; + if (substitutionRecords != null) + { + for (int i = 0; i < substitutionRecords.Count; i++) + { + LigatureSubstitutionRecord record = substitutionRecords[i]; + + uint keyGlyphIndex = record.componentGlyphIDs[0]; + + if (!m_FontFeatureTable.m_LigatureSubstitutionRecordLookup.ContainsKey(keyGlyphIndex)) + m_FontFeatureTable.m_LigatureSubstitutionRecordLookup.Add(keyGlyphIndex, new List {record}); + else + m_FontFeatureTable.m_LigatureSubstitutionRecordLookup[keyGlyphIndex].Add(record); + } + } } internal void InitializeGlyphPaidAdjustmentRecordsLookupDictionary() @@ -697,10 +858,10 @@ internal void InitializeGlyphPaidAdjustmentRecordsLookupDictionary() UpgradeGlyphAdjustmentTableToFontFeatureTable(); // Read Font Features which will include kerning data. - if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary == null) - m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary = new Dictionary(); + if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup == null) + m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup = new Dictionary(); else - m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Clear(); + m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.Clear(); List glyphPairAdjustmentRecords = m_FontFeatureTable.m_GlyphPairAdjustmentRecords; if (glyphPairAdjustmentRecords != null) @@ -709,10 +870,56 @@ internal void InitializeGlyphPaidAdjustmentRecordsLookupDictionary() { TMP_GlyphPairAdjustmentRecord record = glyphPairAdjustmentRecords[i]; - uint key = new GlyphPairKey(record).key; + uint key = record.secondAdjustmentRecord.glyphIndex << 16 | record.firstAdjustmentRecord.glyphIndex; + + if (!m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.ContainsKey(key)) + m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.Add(key, record); + } + } + } + + internal void InitializeMarkToBaseAdjustmentRecordsLookupDictionary() + { + // Read Mark to Base adjustment records + if (m_FontFeatureTable.m_MarkToBaseAdjustmentRecordLookup == null) + m_FontFeatureTable.m_MarkToBaseAdjustmentRecordLookup = new Dictionary(); + else + m_FontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.Clear(); + + List adjustmentRecords = m_FontFeatureTable.m_MarkToBaseAdjustmentRecords; + if (adjustmentRecords != null) + { + for (int i = 0; i < adjustmentRecords.Count; i++) + { + MarkToBaseAdjustmentRecord record = adjustmentRecords[i]; + + uint key = record.markGlyphID << 16 | record.baseGlyphID; + + if (!m_FontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.ContainsKey(key)) + m_FontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.Add(key, record); + } + } + } + + internal void InitializeMarkToMarkAdjustmentRecordsLookupDictionary() + { + // Read Mark to Base adjustment records + if (m_FontFeatureTable.m_MarkToMarkAdjustmentRecordLookup == null) + m_FontFeatureTable.m_MarkToMarkAdjustmentRecordLookup = new Dictionary(); + else + m_FontFeatureTable.m_MarkToMarkAdjustmentRecordLookup.Clear(); + + List adjustmentRecords = m_FontFeatureTable.m_MarkToMarkAdjustmentRecords; + if (adjustmentRecords != null) + { + for (int i = 0; i < adjustmentRecords.Count; i++) + { + MarkToMarkAdjustmentRecord record = adjustmentRecords[i]; - if (!m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.ContainsKey(key)) - m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Add(key, record); + uint key = record.combiningMarkGlyphID << 16 | record.baseMarkGlyphID; + + if (!m_FontFeatureTable.m_MarkToMarkAdjustmentRecordLookup.ContainsKey(key)) + m_FontFeatureTable.m_MarkToMarkAdjustmentRecordLookup.Add(key, record); } } } @@ -723,8 +930,13 @@ internal void AddSynthesizedCharactersAndFaceMetrics() bool isFontFaceLoaded = false; - if (m_AtlasPopulationMode == AtlasPopulationMode.Dynamic) - isFontFaceLoaded = FontEngine.LoadFontFace(sourceFontFile, m_FaceInfo.pointSize) == FontEngineError.Success; + if (m_AtlasPopulationMode == AtlasPopulationMode.Dynamic || m_AtlasPopulationMode == AtlasPopulationMode.DynamicOS) + { + isFontFaceLoaded = LoadFontFace() == FontEngineError.Success; + + if (!isFontFaceLoaded && !InternalDynamicOS && TMP_Settings.warningsDisabled) + Debug.LogWarning("Unable to load font face for [" + this.name + "] font asset.", this); + } // Only characters not present in the source font file will be synthesized. @@ -765,20 +977,6 @@ internal void AddSynthesizedCharactersAndFaceMetrics() // Add Word Joiner / Zero Width Non-Breaking Space \u2060 AddSynthesizedCharacter(0x2060, isFontFaceLoaded); - // Set Cap Line using the capital letter 'X' - if (m_FaceInfo.capLine == 0 && m_CharacterLookupDictionary.ContainsKey('X')) - { - uint glyphIndex = m_CharacterLookupDictionary['X'].glyphIndex; - m_FaceInfo.capLine = m_GlyphLookupDictionary[glyphIndex].metrics.horizontalBearingY; - } - - // Set Mean Line using the lowercase letter 'x' - if (m_FaceInfo.meanLine == 0 && m_CharacterLookupDictionary.ContainsKey('x')) - { - uint glyphIndex = m_CharacterLookupDictionary['x'].glyphIndex; - m_FaceInfo.meanLine = m_GlyphLookupDictionary[glyphIndex].metrics.horizontalBearingY; - } - k_AddSynthesizedCharactersMarker.End(); } @@ -818,14 +1016,48 @@ void AddSynthesizedCharacter(uint unicode, bool isFontFaceLoaded, bool addImmedi m_CharacterLookupDictionary.Add(unicode, new TMP_Character(unicode, this, glyph)); } - internal HashSet FallbackSearchQueryLookup = new HashSet(); + //internal HashSet FallbackSearchQueryLookup = new HashSet(); internal void AddCharacterToLookupCache(uint unicode, TMP_Character character) { m_CharacterLookupDictionary.Add(unicode, character); // Add font asset to fallback references. - FallbackSearchQueryLookup.Add(character.textAsset.instanceID); + //FallbackSearchQueryLookup.Add(character.textAsset.instanceID); + } + + /// + /// + /// + /// + FontEngineError LoadFontFace() + { + if (m_AtlasPopulationMode == AtlasPopulationMode.Dynamic) + { + // Font Asset should have a valid reference to a font in the Editor. + #if UNITY_EDITOR + if (m_SourceFontFile == null) + m_SourceFontFile = SourceFont_EditorRef; + #endif + + return FontEngine.LoadFontFace(m_SourceFontFile, m_FaceInfo.pointSize, m_FaceInfo.faceIndex); + } + + #if UNITY_2020_3_OR_NEWER && !(UNITY_2020_3_1 || UNITY_2020_3_2 || UNITY_2020_3_3 || UNITY_2020_3_4 || UNITY_2020_3_5 || UNITY_2020_3_6 || UNITY_2021_1_1|| UNITY_2021_1_2|| UNITY_2021_1_3|| UNITY_2021_1_4) + // Font Asset is Dynamic OS + #if UNITY_EDITOR + if (SourceFont_EditorRef != null) + { + // Try loading the font face from the referenced source font + if (FontEngine.LoadFontFace(m_SourceFontFile_EditorRef, m_FaceInfo.pointSize, m_FaceInfo.faceIndex) == FontEngineError.Success) + return FontEngineError.Success; + } + #endif + + return FontEngine.LoadFontFace(m_FaceInfo.familyName, m_FaceInfo.styleName, m_FaceInfo.pointSize); + #else + return FontEngineError.Invalid_Face; + #endif } /// @@ -849,6 +1081,8 @@ internal void SortGlyphTable() internal void SortFontFeatureTable() { m_FontFeatureTable.SortGlyphPairAdjustmentRecords(); + m_FontFeatureTable.SortMarkToBaseAdjustmentRecords(); + m_FontFeatureTable.SortMarkToMarkAdjustmentRecords(); } /// @@ -902,7 +1136,7 @@ public bool HasCharacter(char character, bool searchFallbacks = false, bool tryA return true; // Check if font asset is dynamic and if so try to add the requested character to it. - if (tryAddCharacter && m_AtlasPopulationMode == AtlasPopulationMode.Dynamic) + if (tryAddCharacter && (m_AtlasPopulationMode == AtlasPopulationMode.Dynamic || m_AtlasPopulationMode == AtlasPopulationMode.DynamicOS)) { TMP_Character returnedCharacter; @@ -998,7 +1232,7 @@ bool HasCharacter_Internal(uint character, bool searchFallbacks = false, bool tr return true; // Check if fallback is dynamic and if so try to add the requested character to it. - if (tryAddCharacter && atlasPopulationMode == AtlasPopulationMode.Dynamic) + if (tryAddCharacter && (atlasPopulationMode == AtlasPopulationMode.Dynamic || m_AtlasPopulationMode == AtlasPopulationMode.DynamicOS)) { TMP_Character returnedCharacter; @@ -1065,7 +1299,7 @@ public bool HasCharacters(string text, out List missingCharacters) /// Array containing the unicode values of the missing characters. /// Determines if fallback font assets assigned to this font asset should be searched. /// - /// + /// Returns true if all requested characters are available in the font asset and potential fallbacks. public bool HasCharacters(string text, out uint[] missingCharacters, bool searchFallbacks = false, bool tryAddCharacter = false) { missingCharacters = null; @@ -1091,7 +1325,7 @@ public bool HasCharacters(string text, out uint[] missingCharacters, bool search continue; // Check if fallback is dynamic and if so try to add the requested character to it. - if (tryAddCharacter && atlasPopulationMode == AtlasPopulationMode.Dynamic) + if (tryAddCharacter && (atlasPopulationMode == AtlasPopulationMode.Dynamic || m_AtlasPopulationMode == AtlasPopulationMode.DynamicOS)) { TMP_Character returnedCharacter; @@ -1246,10 +1480,7 @@ internal uint GetGlyphIndex(uint unicode) return m_CharacterLookupDictionary[unicode].glyphIndex; // Load font face. - if (FontEngine.LoadFontFace(sourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success) - return 0; - - return FontEngine.GetGlyphIndex(unicode); + return LoadFontFace() == FontEngineError.Success ? FontEngine.GetGlyphIndex(unicode) : 0; } // ================================================================================ @@ -1411,7 +1642,7 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes, bool i } // Load font face. - if (FontEngine.LoadFontFace(m_SourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success) + if (LoadFontFace() != FontEngineError.Success) { missingUnicodes = unicodes.ToArray(); k_TryAddCharactersMarker.End(); @@ -1505,7 +1736,12 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes, bool i // Resize the Atlas Texture to the appropriate size if (m_AtlasTextures[m_AtlasTextureIndex].width == 0 || m_AtlasTextures[m_AtlasTextureIndex].height == 0) { + #if UNITY_2021_2_OR_NEWER + m_AtlasTextures[m_AtlasTextureIndex].Reinitialize(m_AtlasWidth, m_AtlasHeight); + #else m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight); + #endif + FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]); } @@ -1568,10 +1804,7 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes, bool i #if UNITY_EDITOR // Makes the changes to the font asset persistent. - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - TMP_EditorResourceManager.RegisterResourceForUpdate(this); - } + RegisterResourceForUpdate?.Invoke(this); #endif // Populate list of missing characters @@ -1632,7 +1865,7 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo } // Load font face. - if (FontEngine.LoadFontFace(m_SourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success) + if (LoadFontFace() != FontEngineError.Success) { missingCharacters = characters; k_TryAddCharactersMarker.End(); @@ -1726,8 +1959,12 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo // Resize the Atlas Texture to the appropriate size if (m_AtlasTextures[m_AtlasTextureIndex].width == 0 || m_AtlasTextures[m_AtlasTextureIndex].height == 0) { - //Debug.Log("Setting initial size of atlas texture used by font asset [" + this.name + "]."); + #if UNITY_2021_2_OR_NEWER + m_AtlasTextures[m_AtlasTextureIndex].Reinitialize(m_AtlasWidth, m_AtlasHeight); + #else m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight); + #endif + FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]); } @@ -1790,10 +2027,7 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo #if UNITY_EDITOR // Makes the changes to the font asset persistent. - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - TMP_EditorResourceManager.RegisterResourceForUpdate(this); - } + RegisterResourceForUpdate?.Invoke(this); #endif missingCharacters = string.Empty; @@ -1975,7 +2209,7 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) } // Load font face. - if (FontEngine.LoadFontFace(sourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success) + if (LoadFontFace() != FontEngineError.Success) { k_TryAddCharacterMarker.End(); return false; @@ -2017,13 +2251,7 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) #if UNITY_EDITOR // Makes the changes to the font asset persistent. - // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset. - // Could also add some update registry to handle this. - //SortGlyphTable(); - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - TMP_EditorResourceManager.RegisterResourceForUpdate(this); - } + RegisterResourceForUpdate?.Invoke(this); #endif k_TryAddCharacterMarker.End(); @@ -2071,7 +2299,12 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) // Resize the Atlas Texture to the appropriate size if (m_AtlasTextures[m_AtlasTextureIndex].width == 0 || m_AtlasTextures[m_AtlasTextureIndex].height == 0) { + #if UNITY_2021_2_OR_NEWER + m_AtlasTextures[m_AtlasTextureIndex].Reinitialize(m_AtlasWidth, m_AtlasHeight); + #else m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight); + #endif + FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]); } @@ -2098,13 +2331,7 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) #if UNITY_EDITOR // Makes the changes to the font asset persistent. - // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset. - // Could also add some update registry to handle this. - //SortGlyphTable(); - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - TMP_EditorResourceManager.RegisterResourceForUpdate(this); - } + RegisterResourceForUpdate?.Invoke(this); #endif k_TryAddCharacterMarker.End(); @@ -2139,11 +2366,8 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) RegisterFontAssetForFontFeatureUpdate(this); #if UNITY_EDITOR - //SortGlyphTable(); - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - TMP_EditorResourceManager.RegisterResourceForUpdate(this); - } + // Makes the changes to the font asset persistent. + RegisterResourceForUpdate?.Invoke(this); #endif k_TryAddCharacterMarker.End(); @@ -2171,7 +2395,7 @@ internal bool TryGetCharacter_and_QueueRenderToTexture(uint unicode, out TMP_Cha } // Load font face. - if (FontEngine.LoadFontFace(sourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success) + if (LoadFontFace() != FontEngineError.Success) { k_TryAddCharacterMarker.End(); return false; @@ -2213,13 +2437,7 @@ internal bool TryGetCharacter_and_QueueRenderToTexture(uint unicode, out TMP_Cha #if UNITY_EDITOR // Makes the changes to the font asset persistent. - // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset. - // Could also add some update registry to handle this. - //SortGlyphTable(); - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - TMP_EditorResourceManager.RegisterResourceForUpdate(this); - } + RegisterResourceForUpdate?.Invoke(this); #endif k_TryAddCharacterMarker.End(); @@ -2256,14 +2474,9 @@ internal bool TryGetCharacter_and_QueueRenderToTexture(uint unicode, out TMP_Cha RegisterFontAssetForAtlasTextureUpdate(this); #if UNITY_EDITOR + // TODO: Consider potential optimization. This could be handled when exiting Play mode if we added any new characters to the asset. // Makes the changes to the font asset persistent. - // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset. - // Could also add some update registry to handle this. - //SortGlyphTable(); - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - TMP_EditorResourceManager.RegisterResourceForUpdate(this); - } + RegisterResourceForUpdate?.Invoke(this); #endif k_TryAddCharacterMarker.End(); @@ -2409,14 +2622,10 @@ void SetupNewAtlasTexture() #if UNITY_EDITOR // Add new texture as sub asset to font asset - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - Texture2D tex = m_AtlasTextures[m_AtlasTextureIndex]; - tex.name = m_AtlasTexture.name + " " + m_AtlasTextureIndex; + Texture2D tex = m_AtlasTextures[m_AtlasTextureIndex]; + tex.name = m_AtlasTexture.name + " " + m_AtlasTextureIndex; - UnityEditor.AssetDatabase.AddObjectToAsset(m_AtlasTextures[m_AtlasTextureIndex], this); - TMP_EditorResourceManager.RegisterResourceForReimport(this); - } + OnFontAssetTextureChanged?.Invoke(tex, this); #endif } @@ -2440,8 +2649,12 @@ internal void UpdateAtlasTexture() // Resize the Atlas Texture to the appropriate size if (m_AtlasTextures[m_AtlasTextureIndex].width == 0 || m_AtlasTextures[m_AtlasTextureIndex].height == 0) { - //Debug.Log("Setting initial size of atlas texture used by font asset [" + this.name + "]."); - m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight); + #if UNITY_2021_2_OR_NEWER + m_AtlasTextures[m_AtlasTextureIndex].Reinitialize(m_AtlasWidth, m_AtlasHeight); + #else + m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight); + #endif + FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]); } @@ -2486,8 +2699,7 @@ internal void UpdateAtlasTexture() #if UNITY_EDITOR // Makes the changes to the font asset persistent. - //SortAllTables(); - TMP_EditorResourceManager.RegisterResourceForUpdate(this); + RegisterResourceForUpdate?.Invoke(this); #endif } @@ -2524,13 +2736,13 @@ internal void UpdateGlyphAdjustmentRecords() uint pairKey = pairAdjustmentRecords[i].secondAdjustmentRecord.glyphIndex << 16 | pairAdjustmentRecords[i].firstAdjustmentRecord.glyphIndex; // Check if table already contains a pair adjustment record for this key. - if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.ContainsKey(pairKey)) + if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.ContainsKey(pairKey)) continue; TMP_GlyphPairAdjustmentRecord record = new TMP_GlyphPairAdjustmentRecord(pairAdjustmentRecords[i]); m_FontFeatureTable.m_GlyphPairAdjustmentRecords.Add(record); - m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Add(pairKey, record); + m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.Add(pairKey, record); } k_UpdateGlyphAdjustmentRecordsMarker.End(); @@ -2564,13 +2776,13 @@ internal void UpdateGlyphAdjustmentRecords(uint[] glyphIndexes) uint pairKey = pairAdjustmentRecords[i].secondAdjustmentRecord.glyphIndex << 16 | pairAdjustmentRecords[i].firstAdjustmentRecord.glyphIndex; // Check if table already contains a pair adjustment record for this key. - if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.ContainsKey(pairKey)) + if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.ContainsKey(pairKey)) continue; TMP_GlyphPairAdjustmentRecord record = new TMP_GlyphPairAdjustmentRecord(pairAdjustmentRecords[i]); m_FontFeatureTable.m_GlyphPairAdjustmentRecords.Add(record); - m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Add(pairKey, record); + m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.Add(pairKey, record); } k_UpdateGlyphAdjustmentRecordsMarker.End(); @@ -2711,7 +2923,7 @@ public void ClearFontAssetData(bool setAtlasSizeToZero = false) #if UNITY_EDITOR // Makes the changes to the font asset persistent. - TMP_EditorResourceManager.RegisterResourceForUpdate(this); + RegisterResourceForUpdate?.Invoke(this); #endif k_ClearFontAssetDataMarker.End(); @@ -2727,7 +2939,7 @@ internal void ClearFontAssetDataInternal() #if UNITY_EDITOR // Makes the changes to the font asset persistent. - TMP_EditorResourceManager.RegisterResourceForUpdate(this); + RegisterResourceForUpdate?.Invoke(this); #endif } @@ -2793,6 +3005,8 @@ internal void ClearFontAssetTables() // Clear Glyph Adjustment Table if (m_FontFeatureTable != null && m_FontFeatureTable.m_GlyphPairAdjustmentRecords != null) m_FontFeatureTable.glyphPairAdjustmentRecords.Clear(); + + // Clear other tables } /// @@ -2820,8 +3034,7 @@ internal void ClearAtlasTextures(bool setAtlasSizeToZero = false) DestroyImmediate(texture, true); #if UNITY_EDITOR - if (UnityEditor.EditorUtility.IsPersistent(this)) - TMP_EditorResourceManager.RegisterResourceForReimport(this); + RegisterResourceForReimport?.Invoke(this); #endif } @@ -2833,21 +3046,26 @@ internal void ClearAtlasTextures(bool setAtlasSizeToZero = false) // Clear main atlas texture if (texture.isReadable == false) { - #if UNITY_EDITOR && UNITY_2018_4_OR_NEWER && !UNITY_2018_4_0 && !UNITY_2018_4_1 && !UNITY_2018_4_2 && !UNITY_2018_4_3 && !UNITY_2018_4_4 - FontEngineEditorUtilities.SetAtlasTextureIsReadable(texture, true); - #else - Debug.LogWarning("Unable to reset font asset [" + this.name + "]'s atlas texture. Please make the texture [" + texture.name + "] readable.", texture); - return; + #if UNITY_EDITOR + SetAtlasTextureIsReadable?.Invoke(texture, true); #endif } if (setAtlasSizeToZero) { + #if UNITY_2021_2_OR_NEWER + texture.Reinitialize(0, 0, TextureFormat.Alpha8, false); + #else texture.Resize(0, 0, TextureFormat.Alpha8, false); + #endif } else if (texture.width != m_AtlasWidth || texture.height != m_AtlasHeight) { + #if UNITY_2021_2_OR_NEWER + texture.Reinitialize(m_AtlasWidth, m_AtlasHeight, TextureFormat.Alpha8, false); + #else texture.Resize(m_AtlasWidth, m_AtlasHeight, TextureFormat.Alpha8, false); + #endif } // Clear texture atlas @@ -2855,6 +3073,20 @@ internal void ClearAtlasTextures(bool setAtlasSizeToZero = false) texture.Apply(); } + void DestroyAtlasTextures() + { + if (m_AtlasTextures == null) + return; + + for (int i = 0; i < m_AtlasTextures.Length; i++) + { + Texture2D tex = m_AtlasTextures[i]; + + if (tex != null) + DestroyImmediate(tex); + } + } + /// /// Internal method used to upgrade font asset to support Dynamic SDF. /// @@ -2961,18 +3193,6 @@ internal void UpgradeFontAsset() m_GlyphTable.Clear(); m_CharacterTable.Clear(); - //#if UNITY_EDITOR - // TODO: This is causing a crash in Unity and related to AssetDatabase.LoadAssetAtPath and Resources.Load() - // Load font to allow us to get the glyph index. - //string path = UnityEditor.AssetDatabase.GUIDToAssetPath(m_SourceFontFileGUID); - - //if (path != string.Empty) - //{ - //m_SourceFontFile_EditorRef = UnityEditor.AssetDatabase.LoadAssetAtPath(path); - //FontEngine.LoadFontFace(m_SourceFontFile_EditorRef); - //} - //#endif - bool isSpaceCharacterPresent = false; for (int i = 0; i < m_glyphInfoList.Count; i++) { @@ -2982,11 +3202,6 @@ internal void UpgradeFontAsset() uint glyphIndex = (uint)i + 1; - //#if UNITY_EDITOR - //if (m_SourceFontFile_EditorRef != null) - // glyphIndex = FontEngine.GetGlyphIndex((uint)oldGlyph.id); - //#endif - glyph.index = glyphIndex; glyph.glyphRect = new GlyphRect((int)oldGlyph.x, m_AtlasHeight - (int)(oldGlyph.y + oldGlyph.height + 0.5f), (int)(oldGlyph.width + 0.5f), (int)(oldGlyph.height + 0.5f)); glyph.metrics = new GlyphMetrics(oldGlyph.width, oldGlyph.height, oldGlyph.xOffset, oldGlyph.yOffset, oldGlyph.xAdvance); @@ -3020,10 +3235,8 @@ internal void UpgradeFontAsset() // Convert atlas textures data to new format // TODO #if UNITY_EDITOR - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - TMP_EditorResourceManager.RegisterResourceForUpdate(this); - } + // Makes the changes to the font asset persistent. + RegisterResourceForUpdate?.Invoke(this); #endif } @@ -3069,10 +3282,8 @@ void UpgradeGlyphAdjustmentTableToFontFeatureTable() m_KerningTable = null; #if UNITY_EDITOR - if (UnityEditor.EditorUtility.IsPersistent(this)) - { - TMP_EditorResourceManager.RegisterResourceForUpdate(this); - } + // Makes the changes to the font asset persistent. + RegisterResourceForUpdate?.Invoke(this); #endif } diff --git a/Scripts/Runtime/TMP_FontAssetCommon.cs b/Scripts/Runtime/TMP_FontAssetCommon.cs index 0c092aa..c2c67fd 100644 --- a/Scripts/Runtime/TMP_FontAssetCommon.cs +++ b/Scripts/Runtime/TMP_FontAssetCommon.cs @@ -81,9 +81,11 @@ public struct FontAssetCreationSettings { public string sourceFontFileName; public string sourceFontFileGUID; + public int faceIndex; public int pointSizeSamplingMode; public int pointSize; public int padding; + public int paddingMode; public int packingMode; public int atlasWidth; public int atlasHeight; @@ -100,9 +102,11 @@ internal FontAssetCreationSettings(string sourceFontFileGUID, int pointSize, int { this.sourceFontFileName = string.Empty; this.sourceFontFileGUID = sourceFontFileGUID; + this.faceIndex = 0; this.pointSize = pointSize; this.pointSizeSamplingMode = pointSizeSamplingMode; this.padding = padding; + this.paddingMode = 2; this.packingMode = packingMode; this.atlasWidth = atlasWidth; this.atlasHeight = atlasHeight; @@ -228,7 +232,7 @@ public GlyphValueRecord_Legacy secondGlyphAdjustments /// /// Determines if the Character Spacing property of the text object will affect the kerning pair. - /// This is mostly relevant when using Diacritical marks to prevent Character Spacing from altering the + /// This is mostly relevant when using Diacritical marks to prevent Character Spacing from altering the spacing. /// public bool ignoreSpacingAdjustments { @@ -453,4 +457,4 @@ private static TMP_FontAsset SearchForCharacterInternal(List font return null; } } -} \ No newline at end of file +} diff --git a/Scripts/Runtime/TMP_FontAssetUtilities.cs b/Scripts/Runtime/TMP_FontAssetUtilities.cs index b0dfe10..84b2200 100644 --- a/Scripts/Runtime/TMP_FontAssetUtilities.cs +++ b/Scripts/Runtime/TMP_FontAssetUtilities.cs @@ -119,7 +119,7 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM return character; } - if (temp.atlasPopulationMode == AtlasPopulationMode.Dynamic) + if (temp.atlasPopulationMode == AtlasPopulationMode.Dynamic || temp.atlasPopulationMode == AtlasPopulationMode.DynamicOS) { if (temp.TryAddCharacterInternal(unicode, out character)) { @@ -146,7 +146,6 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM // At this point, we were not able to find the requested character in the alternative typeface // so we check the source font asset and its potential fallbacks. } - } #endregion @@ -154,7 +153,7 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM if (sourceFontAsset.characterLookupTable.TryGetValue(unicode, out character)) return character; - if (sourceFontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic) + if (sourceFontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic || sourceFontAsset.atlasPopulationMode == AtlasPopulationMode.DynamicOS) { if (sourceFontAsset.TryAddCharacterInternal(unicode, out character)) return character; @@ -184,7 +183,7 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM continue; // Add reference to this search query - sourceFontAsset.FallbackSearchQueryLookup.Add(id); + //sourceFontAsset.FallbackSearchQueryLookup.Add(id); character = GetCharacterFromFontAsset_Internal(unicode, temp, true, fontStyle, fontWeight, out isAlternativeTypeface); @@ -236,7 +235,7 @@ public static TMP_Character GetCharacterFromFontAssets(uint unicode, TMP_FontAss if (fontAsset == null) continue; // Add reference to this search query - sourceFontAsset.FallbackSearchQueryLookup.Add(fontAsset.instanceID); + //sourceFontAsset.FallbackSearchQueryLookup.Add(fontAsset.instanceID); TMP_Character character = GetCharacterFromFontAsset_Internal(unicode, fontAsset, includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface); diff --git a/Scripts/Runtime/TMP_FontFeatureTable.cs b/Scripts/Runtime/TMP_FontFeatureTable.cs index 633a84e..7413fef 100644 --- a/Scripts/Runtime/TMP_FontFeatureTable.cs +++ b/Scripts/Runtime/TMP_FontFeatureTable.cs @@ -12,6 +12,24 @@ namespace TMPro [Serializable] public class TMP_FontFeatureTable { + /// + /// List that contains the glyph multiple substitution records. + /// + public List multipleSubstitutionRecords + { + get { return m_MultipleSubstitutionRecords; } + set { m_MultipleSubstitutionRecords = value; } + } + + /// + /// List that contains the glyph ligature records. + /// + public List ligatureRecords + { + get { return m_LigatureSubstitutionRecords; } + set { m_LigatureSubstitutionRecords = value; } + } + /// /// List that contains the glyph pair adjustment records. /// @@ -20,13 +38,56 @@ public List glyphPairAdjustmentRecords get { return m_GlyphPairAdjustmentRecords; } set { m_GlyphPairAdjustmentRecords = value; } } - [SerializeField] - internal List m_GlyphPairAdjustmentRecords; /// - /// + /// + /// + public List MarkToBaseAdjustmentRecords + { + get { return m_MarkToBaseAdjustmentRecords; } + set { m_MarkToBaseAdjustmentRecords = value; } + } + + /// + /// /// - internal Dictionary m_GlyphPairAdjustmentRecordLookupDictionary; + public List MarkToMarkAdjustmentRecords + { + get { return m_MarkToMarkAdjustmentRecords; } + set { m_MarkToMarkAdjustmentRecords = value; } + } + + // ============================================= + // Private backing fields for public properties. + // ============================================= + + [SerializeField] + internal List m_MultipleSubstitutionRecords; + + [SerializeField] + internal List m_LigatureSubstitutionRecords; + + [SerializeField] + internal List m_GlyphPairAdjustmentRecords; + + [SerializeField] + internal List m_MarkToBaseAdjustmentRecords; + + [SerializeField] + internal List m_MarkToMarkAdjustmentRecords; + + + // ============================================= + // Lookup data structures. + // ============================================= + + internal Dictionary> m_LigatureSubstitutionRecordLookup; + + internal Dictionary m_GlyphPairAdjustmentRecordLookup; + + internal Dictionary m_MarkToBaseAdjustmentRecordLookup; + + internal Dictionary m_MarkToMarkAdjustmentRecordLookup; // ============================================= // Constructor(s) @@ -34,8 +95,17 @@ public List glyphPairAdjustmentRecords public TMP_FontFeatureTable() { + m_LigatureSubstitutionRecords = new List(); + m_LigatureSubstitutionRecordLookup = new Dictionary>(); + m_GlyphPairAdjustmentRecords = new List(); - m_GlyphPairAdjustmentRecordLookupDictionary = new Dictionary(); + m_GlyphPairAdjustmentRecordLookup = new Dictionary(); + + m_MarkToBaseAdjustmentRecords = new List(); + m_MarkToBaseAdjustmentRecordLookup = new Dictionary(); + + m_MarkToMarkAdjustmentRecords = new List(); + m_MarkToMarkAdjustmentRecordLookup = new Dictionary(); } // ============================================= @@ -51,5 +121,19 @@ public void SortGlyphPairAdjustmentRecords() if (m_GlyphPairAdjustmentRecords.Count > 0) m_GlyphPairAdjustmentRecords = m_GlyphPairAdjustmentRecords.OrderBy(s => s.firstAdjustmentRecord.glyphIndex).ThenBy(s => s.secondAdjustmentRecord.glyphIndex).ToList(); } + + public void SortMarkToBaseAdjustmentRecords() + { + // Sort List of Kerning Info + if (m_MarkToBaseAdjustmentRecords.Count > 0) + m_MarkToBaseAdjustmentRecords = m_MarkToBaseAdjustmentRecords.OrderBy(s => s.baseGlyphID).ThenBy(s => s.markGlyphID).ToList(); + } + + public void SortMarkToMarkAdjustmentRecords() + { + // Sort List of Kerning Info + if (m_MarkToMarkAdjustmentRecords.Count > 0) + m_MarkToMarkAdjustmentRecords = m_MarkToMarkAdjustmentRecords.OrderBy(s => s.baseMarkGlyphID).ThenBy(s => s.combiningMarkGlyphID).ToList(); + } } -} \ No newline at end of file +} diff --git a/Scripts/Runtime/TMP_InputField.cs b/Scripts/Runtime/TMP_InputField.cs index 7edfa6e..d4ea280 100644 --- a/Scripts/Runtime/TMP_InputField.cs +++ b/Scripts/Runtime/TMP_InputField.cs @@ -377,6 +377,21 @@ protected Mesh mesh } } + /// + /// Should the inputfield be automatically activated upon selection. + /// + public virtual bool shouldActivateOnSelect + { + set + { + m_ShouldActivateOnSelect = value; + } + get + { + return m_ShouldActivateOnSelect && Application.platform != RuntimePlatform.tvOS; + } + } + /// /// Should the mobile keyboard input be hidden. /// @@ -754,6 +769,18 @@ public bool resetOnDeActivation private GameObject m_PreviouslySelectedObject; + /// + /// Determines if the text selection will remain visible when the input field looses focus and is deactivated. + /// + public bool keepTextSelectionVisible + { + get { return m_KeepTextSelectionVisible; } + set { m_KeepTextSelectionVisible = value; } + } + + [SerializeField] + private bool m_KeepTextSelectionVisible; + /// /// Controls whether the original text is restored when pressing "ESC". /// @@ -811,8 +838,19 @@ public int lineLimit [SerializeField] protected int m_LineLimit = 0; + /// + /// The type of input expected. See InputField.InputType. + /// public InputType inputType { get { return m_InputType; } set { if (SetPropertyUtility.SetStruct(ref m_InputType, value)) SetToCustom(); } } + /// + /// The TouchScreenKeyboard being used to edit the Input Field. + /// + public TouchScreenKeyboard touchScreenKeyboard { get { return m_SoftKeyboard; } } + + /// + /// They type of mobile keyboard that will be used. + /// public TouchScreenKeyboardType keyboardType { get { return m_KeyboardType; } @@ -823,6 +861,14 @@ public TouchScreenKeyboardType keyboardType } } + /// + /// Determines if the keyboard is opened in alert mode. + /// + public bool isAlert; + + /// + /// The type of validation to perform on a character + /// public CharacterValidation characterValidation { get { return m_CharacterValidation; } set { if (SetPropertyUtility.SetStruct(ref m_CharacterValidation, value)) SetToCustom(); } } /// @@ -838,6 +884,9 @@ public TMP_InputValidator inputValidator public bool readOnly { get { return m_ReadOnly; } set { m_ReadOnly = value; } } + [SerializeField] + private bool m_ShouldActivateOnSelect = true; + public bool richText { get { return m_RichText; } set { m_RichText = value; SetTextComponentRichTextMode(); } } // Derived property @@ -1126,7 +1175,7 @@ private void ON_TEXT_CHANGED(UnityEngine.Object obj) { bool isThisObject = obj == m_TextComponent; - if (isThisObject) + if (isThisObject && !m_IsStringPositionDirty) { if (Application.isPlaying && compositionLength == 0) { @@ -1471,7 +1520,7 @@ protected virtual void LateUpdate() } // Release current selection of selected object is another Input Field - if (selectedObject.GetComponent() != null) + if (m_KeepTextSelectionVisible == false && selectedObject.GetComponent() != null) ReleaseSelection(); return; @@ -1548,21 +1597,21 @@ protected virtual void LateUpdate() if (!m_ReadOnly) text = m_SoftKeyboard.text; - if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.LostFocus) - SendTouchScreenKeyboardStatusChanged(); - - if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Canceled) - { - m_ReleaseSelection = true; - m_WasCanceled = true; - SendTouchScreenKeyboardStatusChanged(); - } - - if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Done) + switch (m_SoftKeyboard.status) { - m_ReleaseSelection = true; - OnSubmit(null); - SendTouchScreenKeyboardStatusChanged(); + case TouchScreenKeyboard.Status.LostFocus: + SendTouchScreenKeyboardStatusChanged(); + break; + case TouchScreenKeyboard.Status.Canceled: + m_ReleaseSelection = true; + m_WasCanceled = true; + SendTouchScreenKeyboardStatusChanged(); + break; + case TouchScreenKeyboard.Status.Done: + m_ReleaseSelection = true; + SendTouchScreenKeyboardStatusChanged(); + OnSubmit(null); + break; } } @@ -2049,11 +2098,11 @@ protected EditState KeyPressed(Event evt) char c = evt.character; // Don't allow return chars or tabulator key to be entered into single line fields. - if (!multiLine && (c == '\t' || c == '\r' || c == 10)) + if (!multiLine && (c == '\t' || c == '\r' || c == '\n')) return EditState.Continue; // Convert carriage return and end-of-text characters to newline. - if (c == '\r' || (int)c == 3) + if (c == '\r' || c == 3) c = '\n'; // Convert Shift Enter to Vertical tab @@ -2116,57 +2165,47 @@ public virtual void OnUpdateSelected(BaseEventData eventData) return; bool consumedEvent = false; - EditState shouldContinue; + EditState editState = EditState.Continue; while (Event.PopEvent(m_ProcessingEvent)) { //Debug.Log("Event: " + m_ProcessingEvent.ToString() + " IsCompositionActive= " + m_IsCompositionActive + " Composition Length: " + compositionLength); - switch (m_ProcessingEvent.rawType) - { - case EventType.KeyUp: - // TODO: Figure out way to handle navigation during IME Composition. - - break; - - - case EventType.KeyDown: - consumedEvent = true; - - // Special handling on OSX which produces more events which need to be suppressed. - if (m_IsCompositionActive && compositionLength == 0) - { - //if (m_ProcessingEvent.keyCode == KeyCode.Backspace && m_ProcessingEvent.modifiers == EventModifiers.None) - //{ - // int eventCount = Event.GetEventCount(); + EventType eventType = m_ProcessingEvent.rawType; - // // Suppress all subsequent events - // for (int i = 0; i < eventCount; i++) - // Event.PopEvent(m_ProcessingEvent); + if (eventType == EventType.KeyUp) + continue; - // break; - //} + if (eventType == EventType.KeyDown) + { + consumedEvent = true; - // Suppress other events related to navigation or termination of composition sequence. - if (m_ProcessingEvent.character == 0 && m_ProcessingEvent.modifiers == EventModifiers.None) - break; - } + // Special handling on OSX which produces more events which need to be suppressed. + if (m_IsCompositionActive && compositionLength == 0) + { + // Suppress other events related to navigation or termination of composition sequence. + if (m_ProcessingEvent.character == 0 && m_ProcessingEvent.modifiers == EventModifiers.None) + continue; + } - shouldContinue = KeyPressed(m_ProcessingEvent); - if (shouldContinue == EditState.Finish) - { - if (!m_WasCanceled) - SendOnSubmit(); + editState = KeyPressed(m_ProcessingEvent); + if (editState == EditState.Finish) + { + if (!m_WasCanceled) + SendOnSubmit(); - DeactivateInputField(); - break; - } + DeactivateInputField(); + break; + } - m_IsTextComponentUpdateRequired = true; - UpdateLabel(); + m_IsTextComponentUpdateRequired = true; + UpdateLabel(); - break; + continue; + } + switch (eventType) + { case EventType.ValidateCommand: case EventType.ExecuteCommand: switch (m_ProcessingEvent.commandName) @@ -2178,6 +2217,7 @@ public virtual void OnUpdateSelected(BaseEventData eventData) } break; } + } if (consumedEvent) @@ -2229,7 +2269,7 @@ float GetScrollPositionRelativeToViewport() // Determine the current scroll position of the text within the viewport Rect viewportRect = m_TextViewport.rect; - float scrollPosition = (m_TextComponent.textInfo.lineInfo[0].ascender - viewportRect.yMax + m_TextComponent.rectTransform.anchoredPosition.y) / ( m_TextComponent.preferredHeight - viewportRect.height); + float scrollPosition = (m_TextComponent.textInfo.lineInfo[0].ascender + m_TextComponent.margin.y + m_TextComponent.margin.w - viewportRect.yMax + m_TextComponent.rectTransform.anchoredPosition.y) / ( m_TextComponent.preferredHeight - viewportRect.height); scrollPosition = (int)((scrollPosition * 1000) + 0.5f) / 1000.0f; @@ -2307,6 +2347,10 @@ private void MoveRight(bool shift, bool ctrl) else { position = m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal].index + m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal].stringLength; + + // Special handling for + if (m_TextComponent.textInfo.characterInfo[position - 1].character == '\r') + position += 1; } } @@ -2378,6 +2422,10 @@ private void MoveLeft(bool shift, bool ctrl) position = caretSelectPositionInternal < 1 ? m_TextComponent.textInfo.characterInfo[0].index : m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 1].index; + + // Special handling for + if (position > 0 && m_TextComponent.textInfo.characterInfo[position - 1].character == '\r') + position -= 1; } } @@ -2905,6 +2953,10 @@ private void DeleteKey() { int numberOfCharactersToRemove = m_TextComponent.textInfo.characterInfo[caretPositionInternal].stringLength; + // Special handling for + if (m_TextComponent.textInfo.characterInfo[caretPositionInternal].character == '\r') + numberOfCharactersToRemove += 1; + // Adjust string position to skip any potential rich text tags. int nextCharacterStringPosition = m_TextComponent.textInfo.characterInfo[caretPositionInternal].index; @@ -2966,17 +3018,25 @@ private void Backspace() { if (caretPositionInternal > 0) { - int numberOfCharactersToRemove = m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1].stringLength; + int caretPositionIndex = caretPositionInternal - 1; + int numberOfCharactersToRemove = m_TextComponent.textInfo.characterInfo[caretPositionIndex].stringLength; + + // Special handling for + if (caretPositionIndex > 0 && m_TextComponent.textInfo.characterInfo[caretPositionIndex - 1].character == '\r') + { + numberOfCharactersToRemove += 1; + caretPositionIndex -= 1; + } // Delete the previous character - m_Text = text.Remove(m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1].index, numberOfCharactersToRemove); + m_Text = text.Remove(m_TextComponent.textInfo.characterInfo[caretPositionIndex].index, numberOfCharactersToRemove); // Get new adjusted string position stringSelectPositionInternal = stringPositionInternal = caretPositionInternal < 1 ? m_TextComponent.textInfo.characterInfo[0].index - : m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1].index; + : m_TextComponent.textInfo.characterInfo[caretPositionIndex].index; - caretSelectPositionInternal = caretPositionInternal = caretPositionInternal - 1; + caretSelectPositionInternal = caretPositionInternal = caretPositionIndex; } m_isLastKeyBackspace = true; @@ -3008,7 +3068,7 @@ protected virtual void Append(string input) { char c = input[i]; - if (c >= ' ' || c == '\t' || c == '\r' || c == 10 || c == '\n') + if (c >= ' ' || c == '\t' || c == '\r' || c == '\n') { Append(c); } @@ -3187,7 +3247,7 @@ protected void SendOnEndTextSelection() protected void SendTouchScreenKeyboardStatusChanged() { - if (onTouchScreenKeyboardStatusChanged != null) + if (m_SoftKeyboard != null && onTouchScreenKeyboardStatusChanged != null) onTouchScreenKeyboardStatusChanged.Invoke(m_SoftKeyboard.status); } @@ -3269,7 +3329,7 @@ protected void UpdateLabel() } } - if (m_IsTextComponentUpdateRequired || m_VerticalScrollbar) + if (m_IsTextComponentUpdateRequired || m_VerticalScrollbar && !(m_IsCaretPositionDirty && m_IsStringPositionDirty)) { m_IsTextComponentUpdateRequired = false; m_TextComponent.ForceMeshUpdate(); @@ -3494,8 +3554,8 @@ private void UpdateGeometry() return; #endif - // No need to draw a cursor on mobile as its handled by the devices keyboard. - if (InPlaceEditing() == false) + // No need to draw a cursor on mobile as its handled by the devices keyboard with the exception of UWP. + if (InPlaceEditing() == false && (Application.platform != RuntimePlatform.WSAPlayerX86 && Application.platform != RuntimePlatform.WSAPlayerX64 && Application.platform != RuntimePlatform.WSAPlayerARM)) return; if (m_CachedInputRenderer == null) @@ -3565,7 +3625,7 @@ private void OnFillVBO(Mesh vbo) } else { - GenerateHightlight(helper, Vector2.zero); + GenerateHighlight(helper, Vector2.zero); SendOnTextSelection(); } @@ -3584,16 +3644,14 @@ private void GenerateCaret(VertexHelper vbo, Vector2 roundingOffset) CreateCursorVerts(); } - float width = m_CaretWidth; - // TODO: Optimize to only update the caret position when needed. Vector2 startPosition = Vector2.zero; float height = 0; TMP_CharacterInfo currentCharacter; - // Make sure caret position does not exceed characterInfo array size. - if (caretPositionInternal >= m_TextComponent.textInfo.characterInfo.Length) + // Make sure caret position does not exceed characterInfo array size or less than zero. + if (caretPositionInternal >= m_TextComponent.textInfo.characterInfo.Length || caretPositionInternal < 0) return; int currentLine = m_TextComponent.textInfo.characterInfo[caretPositionInternal].lineNumber; @@ -3645,8 +3703,11 @@ private void GenerateCaret(VertexHelper vbo, Vector2 roundingOffset) float top = startPosition.y + height; float bottom = top - height; - // Minor tweak to address caret potentially being too thin based on canvas scaler values. - float scale = m_TextComponent.canvas.scaleFactor; + // Compute the width of the caret which is based on the line height of the primary font asset. + //float width = m_CaretWidth; + TMP_FontAsset fontAsset = m_TextComponent.font; + float baseScale = (m_TextComponent.fontSize / fontAsset.m_FaceInfo.pointSize * fontAsset.m_FaceInfo.scale); + float width = m_CaretWidth * fontAsset.faceInfo.lineHeight * baseScale * 0.05f; m_CursorVerts[0].position = new Vector3(startPosition.x, bottom, 0.0f); m_CursorVerts[1].position = new Vector3(startPosition.x, top, 0.0f); @@ -3707,7 +3768,7 @@ private void CreateCursorVerts() } - private void GenerateHightlight(VertexHelper vbo, Vector2 roundingOffset) + private void GenerateHighlight(VertexHelper vbo, Vector2 roundingOffset) { // Update Masking Region UpdateMaskRegions(); @@ -3718,6 +3779,10 @@ private void GenerateHightlight(VertexHelper vbo, Vector2 roundingOffset) TMP_TextInfo textInfo = m_TextComponent.textInfo; + // Return if character count is zero as there is nothing to highlight. + if (textInfo.characterCount == 0) + return; + m_CaretPosition = GetCaretPositionFromStringIndex(stringPositionInternal); m_CaretSelectPosition = GetCaretPositionFromStringIndex(stringSelectPositionInternal); @@ -3778,7 +3843,7 @@ private void GenerateHightlight(VertexHelper vbo, Vector2 roundingOffset) TMP_CharacterInfo endCharInfo = textInfo.characterInfo[currentChar]; // Extra check to handle Carriage Return - if (currentChar > 0 && endCharInfo.character == 10 && textInfo.characterInfo[currentChar - 1].character == 13) + if (currentChar > 0 && endCharInfo.character == '\n' && textInfo.characterInfo[currentChar - 1].character == '\r') endCharInfo = textInfo.characterInfo[currentChar - 1]; Vector2 startPosition = new Vector2(startCharInfo.origin, textInfo.lineInfo[currentLineIndex].ascender); @@ -4074,8 +4139,8 @@ private void ActivateInputFieldInternal() if (shouldHideSoftKeyboard == false && m_ReadOnly == false) { m_SoftKeyboard = (inputType == InputType.Password) ? - TouchScreenKeyboard.Open(m_Text, keyboardType, false, multiLine, true, false, "", characterLimit) : - TouchScreenKeyboard.Open(m_Text, keyboardType, inputType == InputType.AutoCorrect, multiLine, false, false, "", characterLimit); + TouchScreenKeyboard.Open(m_Text, keyboardType, false, multiLine, true, isAlert, "", characterLimit) : + TouchScreenKeyboard.Open(m_Text, keyboardType, inputType == InputType.AutoCorrect, multiLine, false, isAlert, "", characterLimit); OnFocus(); @@ -4117,7 +4182,8 @@ public override void OnSelect(BaseEventData eventData) base.OnSelect(eventData); SendOnFocus(); - ActivateInputField(); + if (shouldActivateOnSelect) + ActivateInputField(); } public virtual void OnPointerClick(PointerEventData eventData) @@ -4310,9 +4376,9 @@ void SetTextComponentWrapMode() return; if (multiLine) - m_TextComponent.enableWordWrapping = true; + m_TextComponent.textWrappingMode = TextWrappingModes.Normal; else - m_TextComponent.enableWordWrapping = false; + m_TextComponent.textWrappingMode = TextWrappingModes.PreserveWhitespaceNoWrap; } // Control Rich Text option on the text component. diff --git a/Scripts/Runtime/TMP_LineInfo.cs b/Scripts/Runtime/TMP_LineInfo.cs index 03ee548..7217ae6 100644 --- a/Scripts/Runtime/TMP_LineInfo.cs +++ b/Scripts/Runtime/TMP_LineInfo.cs @@ -11,6 +11,7 @@ public struct TMP_LineInfo public int characterCount; public int visibleCharacterCount; public int spaceCount; + public int visibleSpaceCount; public int wordCount; public int firstCharacterIndex; public int firstVisibleCharacterIndex; diff --git a/Scripts/Runtime/TMP_MaterialManager.cs b/Scripts/Runtime/TMP_MaterialManager.cs index da5847b..c121c91 100644 --- a/Scripts/Runtime/TMP_MaterialManager.cs +++ b/Scripts/Runtime/TMP_MaterialManager.cs @@ -605,6 +605,9 @@ public static void CopyMaterialPresetProperties(Material source, Material destin float dst_weightNormal = destination.GetFloat(ShaderUtilities.ID_WeightNormal); float dst_weightBold = destination.GetFloat(ShaderUtilities.ID_WeightBold); + // Make sure the same shader is used + destination.shader = source.shader; + // Copy all material properties destination.CopyPropertiesFromMaterial(source); diff --git a/Scripts/Runtime/TMP_MeshInfo.cs b/Scripts/Runtime/TMP_MeshInfo.cs index 665e9e1..c4ce699 100644 --- a/Scripts/Runtime/TMP_MeshInfo.cs +++ b/Scripts/Runtime/TMP_MeshInfo.cs @@ -27,8 +27,19 @@ public struct TMP_MeshInfo public Vector3[] normals; public Vector4[] tangents; - public Vector2[] uvs0; + /// + /// UV0 contains the following information + /// X, Y are the UV coordinates of the glyph in the atlas texture. + /// Z is the texture index in the texture atlas array + /// W is the SDF Scale where a negative value represents bold text + /// + public Vector4[] uvs0; + + /// + /// + /// public Vector2[] uvs2; + //public Vector2[] uvs4; public Color32[] colors32; public int[] triangles; @@ -63,7 +74,7 @@ public TMP_MeshInfo(Mesh mesh, int size) this.vertexCount = 0; this.vertices = new Vector3[sizeX4]; - this.uvs0 = new Vector2[sizeX4]; + this.uvs0 = new Vector4[sizeX4]; this.uvs2 = new Vector2[sizeX4]; //this.uvs4 = new Vector2[sizeX4]; // SDF scale data this.colors32 = new Color32[sizeX4]; @@ -140,7 +151,7 @@ public TMP_MeshInfo(Mesh mesh, int size, bool isVolumetric) this.vertexCount = 0; this.vertices = new Vector3[size_x_s0]; - this.uvs0 = new Vector2[size_x_s0]; + this.uvs0 = new Vector4[size_x_s0]; this.uvs2 = new Vector2[size_x_s0]; //this.uvs4 = new Vector2[sizeX8]; // SDF scale data this.colors32 = new Color32[size_x_s0]; diff --git a/Scripts/Runtime/TMP_PackageResourceImporter.cs b/Scripts/Runtime/TMP_PackageResourceImporter.cs index f2168ea..0f5e3cc 100644 --- a/Scripts/Runtime/TMP_PackageResourceImporter.cs +++ b/Scripts/Runtime/TMP_PackageResourceImporter.cs @@ -8,7 +8,7 @@ namespace TMPro { - [System.Serializable] + [Serializable] public class TMP_PackageResourceImporter { bool m_EssentialResourcesImported; @@ -233,6 +233,150 @@ void SetEditorWindowSize() } } + + [Serializable] + internal class TMP_ShaderPackageImporter + { + bool m_ShadersImported; + + const string s_TMPShaderPackageGUID = "e02b76aaf840d38469530d159da03749"; + + public void OnDestroy() { } + + public void OnGUI() + { + GUILayout.BeginVertical(); + { + // Display options to import Essential resources + GUILayout.BeginVertical(EditorStyles.helpBox); + { + //GUILayout.Label("TextMeshPro Resources Update", EditorStyles.boldLabel); + GUILayout.Label("This release of the TMP package includes updated resources.\n\nPlease use the menu options located in \"Window\\TextMeshPro\\...\" to import the updated\n\"TMP Essential Resources\" and \"TMP Examples & Extras\".\n\nAs usual please be sure to backup any of the files and resources that you may have added or modified in the \"Assets\\TextMesh Pro\\...\" folders.", new GUIStyle(EditorStyles.label) { wordWrap = true } ); + GUILayout.Space(5f); + + GUI.enabled = !m_ShadersImported; + // if (GUILayout.Button("Update TMP Shaders")) + // { + // string packagePath = AssetDatabase.GUIDToAssetPath(s_TMPShaderPackageGUID); + // + // if (string.IsNullOrEmpty(packagePath)) + // return; + // + // AssetDatabase.importPackageCompleted += ImportCallback; + // AssetDatabase.ImportPackage(packagePath, true); + // } + GUILayout.Space(5f); + GUI.enabled = true; + } + GUILayout.EndVertical(); + } + GUILayout.EndVertical(); + GUILayout.Space(5f); + } + + /// + /// + /// + /// + void ImportCallback(string packageName) + { + if (packageName == "TMP Shaders") + { + m_ShadersImported = true; + + EditorWindow window = TMP_ShaderPackageImporterWindow.importerWindow; + + if (window != null) + window.Close(); + } + + Debug.Log("[" + packageName + "] have been imported."); + + AssetDatabase.importPackageCompleted -= ImportCallback; + } + + /// + /// + /// + /// + internal static void ImportShaders(bool interactive) + { + string packagePath = AssetDatabase.GUIDToAssetPath(s_TMPShaderPackageGUID); + + if (string.IsNullOrEmpty(packagePath)) + return; + + AssetDatabase.ImportPackage(packagePath, interactive); + } + } + + + internal class TMP_ShaderPackageImporterWindow : EditorWindow + { + [SerializeField] + TMP_ShaderPackageImporter m_ResourceImporter; + + internal static TMP_ShaderPackageImporterWindow importerWindow; + + public static void ShowPackageImporterWindow() + { + if (importerWindow == null) + { + importerWindow = GetWindow(true, "TextMeshPro Resources Update", true); + } + } + + void OnEnable() + { + // Set Editor Window Size + SetEditorWindowSize(); + + if (m_ResourceImporter == null) + m_ResourceImporter = new TMP_ShaderPackageImporter(); + } + + void OnDestroy() + { + m_ResourceImporter.OnDestroy(); + } + + void OnGUI() + { + Rect p = position; + + if (p.x < 10) + { + p.x = 10; + position = p; + } + + if (p.y < 190) + { + p.y = 190; + position = p; + } + + m_ResourceImporter.OnGUI(); + } + + void OnInspectorUpdate() + { + Repaint(); + } + + /// + /// Limits the minimum size of the editor window. + /// + void SetEditorWindowSize() + { + EditorWindow editorWindow = this; + + Vector2 windowSize = new Vector2(640, 117); + editorWindow.minSize = windowSize; + editorWindow.maxSize = windowSize; + } + } + } #endif diff --git a/Scripts/Runtime/TMP_ResourcesManager.cs b/Scripts/Runtime/TMP_ResourcesManager.cs index b76fa39..64e9a01 100644 --- a/Scripts/Runtime/TMP_ResourcesManager.cs +++ b/Scripts/Runtime/TMP_ResourcesManager.cs @@ -1,5 +1,4 @@ -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using UnityEngine; @@ -10,10 +9,6 @@ namespace TMPro /// public class TMP_ResourceManager { - private static readonly TMP_ResourceManager s_instance = new TMP_ResourceManager(); - - static TMP_ResourceManager() { } - // ====================================================== // TEXT SETTINGS MANAGEMENT // ====================================================== @@ -43,48 +38,182 @@ internal static TMP_Settings GetTextSettings() // FONT ASSET MANAGEMENT - Fields, Properties and Functions // ====================================================== - private static readonly List s_FontAssetReferences = new List(); - private static readonly Dictionary s_FontAssetReferenceLookup = new Dictionary(); + struct FontAssetRef + { + public int nameHashCode; + public int familyNameHashCode; + public int styleNameHashCode; + public long familyNameAndStyleHashCode; + public readonly TMP_FontAsset fontAsset; + + public FontAssetRef(int nameHashCode, int familyNameHashCode, int styleNameHashCode, TMP_FontAsset fontAsset) + { + this.nameHashCode = nameHashCode; + this.familyNameHashCode = familyNameHashCode; + this.styleNameHashCode = styleNameHashCode; + this.familyNameAndStyleHashCode = (long) styleNameHashCode << 32 | (uint) familyNameHashCode; + this.fontAsset = fontAsset; + } + } + + static readonly Dictionary s_FontAssetReferences = new Dictionary(); + static readonly Dictionary s_FontAssetNameReferenceLookup = new Dictionary(); + static readonly Dictionary s_FontAssetFamilyNameAndStyleReferenceLookup = new Dictionary(); + static readonly List s_FontAssetRemovalList = new List(16); + + static readonly int k_RegularStyleHashCode = TMP_TextUtilities.GetHashCode("Regular"); /// - /// + /// Add font asset to resource manager. /// - /// + /// Font asset to be added to the resource manager. public static void AddFontAsset(TMP_FontAsset fontAsset) { - int hashcode = fontAsset.hashCode; + int instanceID = fontAsset.instanceID; + + if (!s_FontAssetReferences.ContainsKey(instanceID)) + { + FontAssetRef fontAssetRef = new FontAssetRef(fontAsset.hashCode, fontAsset.familyNameHashCode, fontAsset.styleNameHashCode, fontAsset); + s_FontAssetReferences.Add(instanceID, fontAssetRef); + + // Add font asset to name reference lookup + if (!s_FontAssetNameReferenceLookup.ContainsKey(fontAssetRef.nameHashCode)) + s_FontAssetNameReferenceLookup.Add(fontAssetRef.nameHashCode, fontAsset); - if (s_FontAssetReferenceLookup.ContainsKey(hashcode)) - return; + // Add font asset to family name and style lookup + if (!s_FontAssetFamilyNameAndStyleReferenceLookup.ContainsKey(fontAssetRef.familyNameAndStyleHashCode)) + s_FontAssetFamilyNameAndStyleReferenceLookup.Add(fontAssetRef.familyNameAndStyleHashCode, fontAsset); + } + else + { + FontAssetRef fontAssetRef = s_FontAssetReferences[instanceID]; + + // Return if font asset name, family and style name have not changed. + if (fontAssetRef.nameHashCode == fontAsset.hashCode && fontAssetRef.familyNameHashCode == fontAsset.familyNameHashCode && fontAssetRef.styleNameHashCode == fontAsset.styleNameHashCode) + return; + + // Check if font asset name has changed + if (fontAssetRef.nameHashCode != fontAsset.hashCode) + { + s_FontAssetNameReferenceLookup.Remove(fontAssetRef.nameHashCode); + + fontAssetRef.nameHashCode = fontAsset.hashCode; + + if (!s_FontAssetNameReferenceLookup.ContainsKey(fontAssetRef.nameHashCode)) + s_FontAssetNameReferenceLookup.Add(fontAssetRef.nameHashCode, fontAsset); + } + + // Check if family or style name has changed + if (fontAssetRef.familyNameHashCode != fontAsset.familyNameHashCode || fontAssetRef.styleNameHashCode != fontAsset.styleNameHashCode) + { + s_FontAssetFamilyNameAndStyleReferenceLookup.Remove(fontAssetRef.familyNameAndStyleHashCode); + + fontAssetRef.familyNameHashCode = fontAsset.familyNameHashCode; + fontAssetRef.styleNameHashCode = fontAsset.styleNameHashCode; + fontAssetRef.familyNameAndStyleHashCode = (long) fontAsset.styleNameHashCode << 32 | (uint) fontAsset.familyNameHashCode; - s_FontAssetReferences.Add(fontAsset); - s_FontAssetReferenceLookup.Add(hashcode, fontAsset); + if (!s_FontAssetFamilyNameAndStyleReferenceLookup.ContainsKey(fontAssetRef.familyNameAndStyleHashCode)) + s_FontAssetFamilyNameAndStyleReferenceLookup.Add(fontAssetRef.familyNameAndStyleHashCode, fontAsset); + } + + s_FontAssetReferences[instanceID] = fontAssetRef; + } } /// - /// + /// Remove font asset from resource manager. + /// + /// Font asset to be removed from the resource manager. + // public static void RemoveFontAsset(TMP_FontAsset fontAsset) + // { + // int hashCode = fontAsset.hashCode; + // + // if (s_FontAssetNameReferenceLookup.ContainsKey(hashCode)) + // { + // s_FontAssetNameReferenceLookup.Remove(hashCode); + // s_FontAssetReferences.Remove(fontAsset); + // } + // } + + /// + /// Try getting a reference to the font asset using the hash code calculated from its file name. /// - /// + /// /// /// - public static bool TryGetFontAsset(int hashcode, out TMP_FontAsset fontAsset) + internal static bool TryGetFontAssetByName(int nameHashcode, out TMP_FontAsset fontAsset) { fontAsset = null; - return s_FontAssetReferenceLookup.TryGetValue(hashcode, out fontAsset); + return s_FontAssetNameReferenceLookup.TryGetValue(nameHashcode, out fontAsset); } + /// + /// Try getting a reference to the font asset using the hash code calculated from font's family and style name. + /// + /// + /// + /// + /// + internal static bool TryGetFontAssetByFamilyName(int familyNameHashCode, int styleNameHashCode, out TMP_FontAsset fontAsset) + { + fontAsset = null; + + if (styleNameHashCode == 0) + styleNameHashCode = k_RegularStyleHashCode; + + long familyAndStyleNameHashCode = (long) styleNameHashCode << 32 | (uint) familyNameHashCode; + + return s_FontAssetFamilyNameAndStyleReferenceLookup.TryGetValue(familyAndStyleNameHashCode, out fontAsset); + } - internal static void RebuildFontAssetCache(int instanceID) + /// + /// + /// + internal static void RebuildFontAssetCache() { // Iterate over loaded font assets to update affected font assets - for (int i = 0; i < s_FontAssetReferences.Count; i++) + foreach (var pair in s_FontAssetReferences) { - TMP_FontAsset fontAsset = s_FontAssetReferences[i]; + FontAssetRef fontAssetRef = pair.Value; + + TMP_FontAsset fontAsset = fontAssetRef.fontAsset; - if (fontAsset.FallbackSearchQueryLookup.Contains(instanceID)) - fontAsset.ReadFontAssetDefinition(); + if (fontAsset == null) + { + // Remove font asset from our lookup dictionaries + s_FontAssetNameReferenceLookup.Remove(fontAssetRef.nameHashCode); + s_FontAssetFamilyNameAndStyleReferenceLookup.Remove(fontAssetRef.familyNameAndStyleHashCode); + + // Add font asset to our removal list + s_FontAssetRemovalList.Add(pair.Key); + continue; + } + + fontAsset.InitializeCharacterLookupDictionary(); + fontAsset.AddSynthesizedCharactersAndFaceMetrics(); + } + + // Remove font assets in our removal list from our font asset references + for (int i = 0; i < s_FontAssetRemovalList.Count; i++) + { + s_FontAssetReferences.Remove(s_FontAssetRemovalList[i]); } + s_FontAssetRemovalList.Clear(); + + TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, null); } + + // internal static void RebuildFontAssetCache(int instanceID) + // { + // // Iterate over loaded font assets to update affected font assets + // for (int i = 0; i < s_FontAssetReferences.Count; i++) + // { + // TMP_FontAsset fontAsset = s_FontAssetReferences[i]; + // + // if (fontAsset.FallbackSearchQueryLookup.Contains(instanceID)) + // fontAsset.ReadFontAssetDefinition(); + // } + // } } } diff --git a/Scripts/Runtime/TMP_RichTextTagsCommon.cs b/Scripts/Runtime/TMP_RichTextTagsCommon.cs index dcc283e..7908375 100644 --- a/Scripts/Runtime/TMP_RichTextTagsCommon.cs +++ b/Scripts/Runtime/TMP_RichTextTagsCommon.cs @@ -5,11 +5,10 @@ namespace TMPro { /// - /// Rich Text Tags and Attribute definitions and their respective HashCode values. + /// Rich text markup tags and attribute definitions and respective hash code values. /// - internal enum MarkupTag : int + internal enum MarkupTag { - // Rich Text Tags BOLD = 66, // SLASH_BOLD = 1613, // ITALIC = 73, // @@ -81,6 +80,15 @@ internal enum MarkupTag : int ROTATE = -1000007783, // SLASH_ROTATE = -764695562, // + TABLE = 226476955, // + SLASH_TABLE = -979118220, //
+ TH = 5862489, // + SLASH_TH = 193346070, // + TR = 5862467, // + SLASH_TR = 193346060, // + TD = 5862485, // + SLASH_TD = 193346074, // + LOWERCASE = -1506899689, // SLASH_LOWERCASE = -1451284584, // ALLCAPS = 218273952, // @@ -104,6 +112,7 @@ internal enum MarkupTag : int MATERIAL = 825491659, // HREF = 2535353, // text to be displayed. ANGLE = 75347905, // Italic Slant Angle + PADDING = -2144568463, // Named Colors RED = 91635, diff --git a/Scripts/Runtime/TMP_Settings.cs b/Scripts/Runtime/TMP_Settings.cs index 32f0180..70feb14 100644 --- a/Scripts/Runtime/TMP_Settings.cs +++ b/Scripts/Runtime/TMP_Settings.cs @@ -1,4 +1,5 @@ using UnityEngine; +using UnityEngine.Serialization; using System.Collections; using System.Collections.Generic; @@ -30,14 +31,15 @@ public static string version } /// - /// Controls if Word Wrapping will be enabled on newly created text objects by default. + /// Controls the text wrapping mode of newly created text objects. /// - public static bool enableWordWrapping + public static TextWrappingModes textWrappingMode { - get { return instance.m_enableWordWrapping; } + get { return instance.m_TextWrappingMode; } } + [FormerlySerializedAs("m_enableWordWrapping")] [SerializeField] - private bool m_enableWordWrapping; + private TextWrappingModes m_TextWrappingMode; /// /// Controls if Kerning is enabled on newly created text objects by default. @@ -110,6 +112,16 @@ public static int missingGlyphCharacter [SerializeField] private int m_missingGlyphCharacter; + /// + /// Determines if the "Clear Dynamic Data on Build" property will be set to true or false on newly created dynamic font assets. + /// + public static bool clearDynamicDataOnBuild + { + get { return instance.m_ClearDynamicDataOnBuild; } + } + + [SerializeField] private bool m_ClearDynamicDataOnBuild = true; + /// /// Controls the display of warning message in the console. /// @@ -232,6 +244,15 @@ public static bool matchMaterialPreset [SerializeField] private bool m_matchMaterialPreset; + /// + /// Determines if sub text objects will be hidden in the scene hierarchy. + /// + public static bool hideSubTextObjects + { + get { return instance.m_HideSubTextObjects; } + } + [SerializeField] private bool m_HideSubTextObjects = true; + /// /// The Default Sprite Asset to be used by default. /// diff --git a/Scripts/Runtime/TMP_ShaderUtilities.cs b/Scripts/Runtime/TMP_ShaderUtilities.cs index be9a090..17211c8 100644 --- a/Scripts/Runtime/TMP_ShaderUtilities.cs +++ b/Scripts/Runtime/TMP_ShaderUtilities.cs @@ -15,6 +15,36 @@ public static class ShaderUtilities public static int ID_FaceDilate; public static int ID_Shininess; + /// + /// Property ID for the _OutlineOffset1 shader property used by URP and HDRP shaders + /// + public static int ID_OutlineOffset1; + + /// + /// Property ID for the _OutlineOffset2 shader property used by URP and HDRP shaders + /// + public static int ID_OutlineOffset2; + + /// + /// Property ID for the _OutlineOffset3 shader property used by URP and HDRP shaders + /// + public static int ID_OutlineOffset3; + + /// + /// Property ID for the ID_AdditiveOutlineMode shader property used by URP and HDRP shaders + /// + public static int ID_OutlineMode; + + /// + /// Property ID for the _IsoPerimeter shader property used by URP and HDRP shaders + /// + public static int ID_IsoPerimeter; + + /// + /// Property ID for the _Softness shader property used by URP and HDRP shaders + /// + public static int ID_Softness; + public static int ID_UnderlayColor; public static int ID_UnderlayOffsetX; public static int ID_UnderlayOffsetY; @@ -159,6 +189,14 @@ public static void GetShaderPropertyIDs() ID_FaceDilate = Shader.PropertyToID("_FaceDilate"); ID_Shininess = Shader.PropertyToID("_FaceShininess"); + ID_OutlineOffset1 = Shader.PropertyToID("_OutlineOffset1"); + ID_OutlineOffset2 = Shader.PropertyToID("_OutlineOffset2"); + ID_OutlineOffset3 = Shader.PropertyToID("_OutlineOffset3"); + ID_OutlineMode = Shader.PropertyToID("_OutlineMode"); + + ID_IsoPerimeter = Shader.PropertyToID("_IsoPerimeter"); + ID_Softness = Shader.PropertyToID("_Softness"); + ID_UnderlayColor = Shader.PropertyToID("_UnderlayColor"); ID_UnderlayOffsetX = Shader.PropertyToID("_UnderlayOffsetX"); ID_UnderlayOffsetY = Shader.PropertyToID("_UnderlayOffsetY"); @@ -365,6 +403,12 @@ public static float GetPadding(Material material, bool enableExtraPadding, bool return extraPadding + 1.0f; } + // Special handling for new SRP Shaders + if (material.HasProperty(ID_IsoPerimeter)) + { + return ComputePaddingForProperties(material) + 0.25f + extraPadding; + } + Vector4 padding = Vector4.zero; Vector4 maxPadding = Vector4.zero; @@ -379,6 +423,7 @@ public static float GetPadding(Material material, bool enableExtraPadding, bool float glowOffset = 0; float glowOuter = 0; + float gradientScale = 0; float uniformPadding = 0; // Iterate through each of the assigned materials to find the max values to set the padding. @@ -421,10 +466,28 @@ public static float GetPadding(Material material, bool enableExtraPadding, bool if (material.HasProperty(ID_ScaleRatio_C)) scaleRatio_C = material.GetFloat(ID_ScaleRatio_C); - float offsetX = material.GetFloat(ID_UnderlayOffsetX) * scaleRatio_C; - float offsetY = material.GetFloat(ID_UnderlayOffsetY) * scaleRatio_C; - float dilate = material.GetFloat(ID_UnderlayDilate) * scaleRatio_C; - float softness = material.GetFloat(ID_UnderlaySoftness) * scaleRatio_C; + float offsetX = 0; + float offsetY = 0; + float dilate = 0; + float softness = 0; + + if (material.HasProperty(ID_UnderlayOffset)) + { + Vector2 underlayOffset = material.GetVector(ID_UnderlayOffset); + offsetX = underlayOffset.x; + offsetY = underlayOffset.y; + + dilate = material.GetFloat(ID_UnderlayDilate); + softness = material.GetFloat(ID_UnderlaySoftness); + } + else if (material.HasProperty(ID_UnderlayOffsetX)) + { + + offsetX = material.GetFloat(ID_UnderlayOffsetX) * scaleRatio_C; + offsetY = material.GetFloat(ID_UnderlayOffsetY) * scaleRatio_C; + dilate = material.GetFloat(ID_UnderlayDilate) * scaleRatio_C; + softness = material.GetFloat(ID_UnderlaySoftness) * scaleRatio_C; + } padding.x = Mathf.Max(padding.x, faceDilate + dilate + softness - offsetX); padding.y = Mathf.Max(padding.y, faceDilate + dilate + softness - offsetY); @@ -452,7 +515,7 @@ public static float GetPadding(Material material, bool enableExtraPadding, bool maxPadding.z = maxPadding.z < padding.z ? padding.z : maxPadding.z; maxPadding.w = maxPadding.w < padding.w ? padding.w : maxPadding.w; - float gradientScale = material.GetFloat(ID_GradientScale); + gradientScale = material.GetFloat(ID_GradientScale); padding *= gradientScale; // Set UniformPadding to the maximum value of any of its components. @@ -464,6 +527,48 @@ public static float GetPadding(Material material, bool enableExtraPadding, bool } + static float ComputePaddingForProperties(Material mat) + { + Vector4 dilation = mat.GetVector(ID_IsoPerimeter); + Vector2 outlineOffset1 = mat.GetVector(ID_OutlineOffset1); + Vector2 outlineOffset2 = mat.GetVector(ID_OutlineOffset2); + Vector2 outlineOffset3 = mat.GetVector(ID_OutlineOffset3); + bool isOutlineModeEnabled = mat.GetFloat(ID_OutlineMode) != 0; + + Vector4 softness = mat.GetVector(ID_Softness); + float gradientScale = mat.GetFloat(ID_GradientScale); + + // Face + float padding = Mathf.Max(0, dilation.x + softness.x * 0.5f); + + // Outlines + if (!isOutlineModeEnabled) + { + padding = Mathf.Max(padding, dilation.y + softness.y * 0.5f + Mathf.Max(Mathf.Abs(outlineOffset1.x), Mathf.Abs(outlineOffset1.y))); + padding = Mathf.Max(padding, dilation.z + softness.z * 0.5f + Mathf.Max(Mathf.Abs(outlineOffset2.x), Mathf.Abs(outlineOffset2.y))); + padding = Mathf.Max(padding, dilation.w + softness.w * 0.5f + Mathf.Max(Mathf.Abs(outlineOffset3.x), Mathf.Abs(outlineOffset3.y))); + } + else + { + float offsetOutline1 = Mathf.Max(Mathf.Abs(outlineOffset1.x), Mathf.Abs(outlineOffset1.y)); + float offsetOutline2 = Mathf.Max(Mathf.Abs(outlineOffset2.x), Mathf.Abs(outlineOffset2.y)); + + padding = Mathf.Max(padding, dilation.y + softness.y * 0.5f + offsetOutline1); + padding = Mathf.Max(padding, dilation.z + softness.z * 0.5f + offsetOutline2); + + float maxOffset = Mathf.Max(offsetOutline1, offsetOutline2); + padding += Mathf.Max(0 ,(dilation.w + softness.w * 0.5f) - Mathf.Max(0, padding - maxOffset)); + } + + // Underlay + Vector2 underlayOffset = mat.GetVector(ID_UnderlayOffset); + float underlayDilation = mat.GetFloat(ID_UnderlayDilate); + float underlaySoftness = mat.GetFloat(ID_UnderlaySoftness); + padding = Mathf.Max(padding, underlayDilation + underlaySoftness * 0.5f + Mathf.Max(Mathf.Abs(underlayOffset.x), Mathf.Abs(underlayOffset.y))); + + return padding * gradientScale; + } + // Function to determine how much extra padding is required as a result of material properties like dilate, outline thickness, softness, glow, etc... public static float GetPadding(Material[] materials, bool enableExtraPadding, bool isBold) { diff --git a/Scripts/Runtime/TMP_SpriteAnimator.cs b/Scripts/Runtime/TMP_SpriteAnimator.cs index e02a210..6e64a26 100644 --- a/Scripts/Runtime/TMP_SpriteAnimator.cs +++ b/Scripts/Runtime/TMP_SpriteAnimator.cs @@ -113,7 +113,7 @@ IEnumerator DoSpriteAnimationInternal(int currentCharacter, TMP_SpriteAsset spri vertices[vertexIndex + 3] = br; // Update the UV to point to the new sprite - Vector2[] uvs0 = meshInfo.uvs0; + Vector4[] uvs0 = meshInfo.uvs0; Vector2 uv0 = new Vector2((float)spriteCharacter.glyph.glyphRect.x / spriteAsset.spriteSheet.width, (float)spriteCharacter.glyph.glyphRect.y / spriteAsset.spriteSheet.height); Vector2 uv1 = new Vector2(uv0.x, (float)(spriteCharacter.glyph.glyphRect.y + spriteCharacter.glyph.glyphRect.height) / spriteAsset.spriteSheet.height); @@ -127,7 +127,7 @@ IEnumerator DoSpriteAnimationInternal(int currentCharacter, TMP_SpriteAsset spri // Update the modified vertex attributes meshInfo.mesh.vertices = vertices; - meshInfo.mesh.uv = uvs0; + meshInfo.mesh.SetUVs(0, uvs0); m_TextComponent.UpdateGeometry(meshInfo.mesh, materialIndex); diff --git a/Scripts/Runtime/TMP_SpriteAsset.cs b/Scripts/Runtime/TMP_SpriteAsset.cs index 007ca42..c241d5e 100644 --- a/Scripts/Runtime/TMP_SpriteAsset.cs +++ b/Scripts/Runtime/TMP_SpriteAsset.cs @@ -12,18 +12,6 @@ public class TMP_SpriteAsset : TMP_Asset internal Dictionary m_NameLookup; internal Dictionary m_GlyphIndexLookup; - /// - /// The version of the sprite asset class. - /// Version 1.1.0 updates the asset data structure to be compatible with new font asset structure. - /// - public string version - { - get { return m_Version; } - internal set { m_Version = value; } - } - [SerializeField] - private string m_Version; - /// /// Information about the sprite asset's face. /// @@ -197,7 +185,7 @@ public void UpdateLookupTables() spriteCharacter.glyph = m_SpriteGlyphLookup[glyphIndex]; spriteCharacter.textAsset = this; - int nameHashCode = m_SpriteCharacterTable[i].hashCode; + int nameHashCode = TMP_TextUtilities.GetHashCode(m_SpriteCharacterTable[i].name); if (m_NameLookup.ContainsKey(nameHashCode) == false) m_NameLookup.Add(nameHashCode, i); diff --git a/Scripts/Runtime/TMP_SpriteCharacter.cs b/Scripts/Runtime/TMP_SpriteCharacter.cs index 51faacb..77aeb38 100644 --- a/Scripts/Runtime/TMP_SpriteCharacter.cs +++ b/Scripts/Runtime/TMP_SpriteCharacter.cs @@ -21,17 +21,9 @@ public string name return; m_Name = value; - m_HashCode = TMP_TextParsingUtilities.GetHashCodeCaseSensitive(m_Name); } } - /// - /// The hashcode value which is computed from the name of the sprite element. - /// This value is read-only and updated when the name of the text sprite is changed. - /// - public int hashCode { get { return m_HashCode; } } - - // ============================================= // Private backing fields for public properties. // ============================================= @@ -39,10 +31,6 @@ public string name [SerializeField] private string m_Name; - [SerializeField] - private int m_HashCode; - - // ******************** // CONSTRUCTORS // ******************** diff --git a/Scripts/Runtime/TMP_SubMesh.cs b/Scripts/Runtime/TMP_SubMesh.cs index 810c7ee..f213ce1 100644 --- a/Scripts/Runtime/TMP_SubMesh.cs +++ b/Scripts/Runtime/TMP_SubMesh.cs @@ -235,7 +235,7 @@ public TMP_Text textComponent public static TMP_SubMesh AddSubTextObject(TextMeshPro textComponent, MaterialReference materialReference) { GameObject go = new GameObject("TMP SubMesh [" + materialReference.material.name + "]", typeof(TMP_SubMesh)); - go.hideFlags = HideFlags.DontSave; + go.hideFlags = TMP_Settings.hideSubTextObjects ? HideFlags.HideAndDontSave : HideFlags.DontSave; TMP_SubMesh subMesh = go.GetComponent(); @@ -426,7 +426,7 @@ void ON_SPRITE_ASSET_PROPERTY_CHANGED(bool isChanged, UnityEngine.Object obj) // Event received when font asset properties are changed in Font Inspector void ON_FONT_PROPERTY_CHANGED(bool isChanged, Object fontAsset) { - if (m_fontAsset != null && fontAsset.GetInstanceID() == m_fontAsset.GetInstanceID()) + if (m_fontAsset != null && fontAsset != null && fontAsset.GetInstanceID() == m_fontAsset.GetInstanceID()) { // Copy Normal and Bold Weight if (m_fallbackMaterial != null) diff --git a/Scripts/Runtime/TMP_SubMeshUI.cs b/Scripts/Runtime/TMP_SubMeshUI.cs index c4431f6..36ce900 100644 --- a/Scripts/Runtime/TMP_SubMeshUI.cs +++ b/Scripts/Runtime/TMP_SubMeshUI.cs @@ -213,7 +213,7 @@ public TMP_Text textComponent public static TMP_SubMeshUI AddSubTextObject(TextMeshProUGUI textComponent, MaterialReference materialReference) { GameObject go = new GameObject("TMP UI SubObject [" + materialReference.material.name + "]", typeof(RectTransform)); - go.hideFlags = HideFlags.DontSave; + go.hideFlags = TMP_Settings.hideSubTextObjects ? HideFlags.HideAndDontSave : HideFlags.DontSave; go.transform.SetParent(textComponent.transform, false); go.transform.SetAsFirstSibling(); @@ -463,7 +463,7 @@ void ON_SPRITE_ASSET_PROPERTY_CHANGED(bool isChanged, UnityEngine.Object obj) // Event received when font asset properties are changed in Font Inspector void ON_FONT_PROPERTY_CHANGED(bool isChanged, Object fontAsset) { - if (m_fontAsset != null && fontAsset.GetInstanceID() == m_fontAsset.GetInstanceID()) + if (m_fontAsset != null && fontAsset != null && fontAsset.GetInstanceID() == m_fontAsset.GetInstanceID()) { // Copy Normal and Bold Weight if (m_fallbackMaterial != null) diff --git a/Scripts/Runtime/TMP_Text.cs b/Scripts/Runtime/TMP_Text.cs index f96141c..0e6debf 100644 --- a/Scripts/Runtime/TMP_Text.cs +++ b/Scripts/Runtime/TMP_Text.cs @@ -1,11 +1,11 @@ #define TMP_PRESENT using System; -using System.Collections; using System.Text; using System.Collections.Generic; using Unity.Profiling; using UnityEngine; +using UnityEngine.Serialization; using UnityEngine.TextCore; using UnityEngine.UI; @@ -96,6 +96,7 @@ public enum TextRenderFlags public enum TMP_TextElementType { Character, Sprite }; public enum MaskingTypes { MaskOff = 0, MaskHard = 1, MaskSoft = 2 }; //, MaskTex = 4 }; public enum TextOverflowModes { Overflow = 0, Ellipsis = 1, Masking = 2, Truncate = 3, ScrollRect = 4, Page = 5, Linked = 6 }; + public enum TextWrappingModes { NoWrap = 0, Normal = 1, PreserveWhitespace = 2, PreserveWhitespaceNoWrap = 3 }; public enum MaskingOffsetMode { Percentage = 0, Pixel = 1 }; public enum TextureMappingOptions { Character = 0, Line = 1, Paragraph = 2, MatchAspect = 3 }; @@ -266,6 +267,8 @@ public override Color color protected static Color32 s_colorWhite = new Color32(255, 255, 255, 255); protected Color32 m_underlineColor = s_colorWhite; protected Color32 m_strikethroughColor = s_colorWhite; + internal HighlightState m_HighlightState = new HighlightState(s_colorWhite, TMP_Offset.zero); + internal bool m_ConvertToLinearSpace; /// /// Sets the vertex color alpha value. @@ -711,16 +714,35 @@ public float characterWidthAdjustment protected float m_charWidthAdjDelta = 0; + /// + /// Controls the text wrapping mode. + /// + public TextWrappingModes textWrappingMode + { + get { return m_TextWrappingMode; } + set { if (m_TextWrappingMode == value) return; m_havePropertiesChanged = true; m_TextWrappingMode = value; SetVerticesDirty(); SetLayoutDirty(); } + } + + /// /// Controls whether or not word wrapping is applied. When disabled, the text will be displayed on a single line. /// + [Obsolete("The enabledWordWrapping property is now obsolete. Please use the textWrappingMode property instead.")] public bool enableWordWrapping { - get { return m_enableWordWrapping; } - set { if (m_enableWordWrapping == value) return; m_havePropertiesChanged = true; m_enableWordWrapping = value; SetVerticesDirty(); SetLayoutDirty(); } + get { return m_TextWrappingMode != 0; } + set + { + TextWrappingModes mode = (TextWrappingModes)(value ? 1 : 0); + + if (m_TextWrappingMode == mode) + return; + + m_havePropertiesChanged = true; m_TextWrappingMode = mode; SetVerticesDirty(); SetLayoutDirty(); + } } - [SerializeField] - protected bool m_enableWordWrapping = false; + [SerializeField] [FormerlySerializedAs("m_enableWordWrapping")] + protected TextWrappingModes m_TextWrappingMode; protected bool m_isCharacterWrappingEnabled = false; protected bool m_isNonBreakingSpace = false; protected bool m_isIgnoringAlignment; @@ -841,7 +863,7 @@ public bool enableKerning } [SerializeField] protected bool m_enableKerning; - protected float m_GlyphHorizontalAdvanceAdjustment; + protected int m_LastBaseGlyphIndex; /// /// Adds extra padding around each character. This may be necessary when the displayed text is very small to prevent clipping. @@ -1125,7 +1147,13 @@ public virtual Vector4 margin /// public TMP_TextInfo textInfo { - get { return m_textInfo; } + get + { + if (m_textInfo == null) + m_textInfo = new TMP_TextInfo(this); + + return m_textInfo; + } } //[SerializeField] protected TMP_TextInfo m_textInfo; // Class which holds information about the Text object such as characters, lines, mesh data as well as metrics. @@ -1265,6 +1293,21 @@ public Bounds textBounds /// public static event Func OnSpriteAssetRequest; + /// + /// Delegate for the OnMissingCharacter event called when the requested Unicode character is missing from the font asset. + /// + /// The Unicode of the missing character. + /// The index of the missing character in the source string. + /// The source text that contains the missing character. + /// The font asset that is missing the requested characters. + /// The text component where the requested character is missing. + public delegate void MissingCharacterEventCallback(int unicode, int stringIndex, string text, TMP_FontAsset fontAsset, TMP_Text textComponent); + + /// + /// Event delegate to be called when the requested Unicode character is missing from the font asset. + /// + public static event MissingCharacterEventCallback OnMissingCharacter; + /// /// Event delegate to allow modifying the text geometry before it is uploaded to the mesh and rendered. /// @@ -1383,7 +1426,7 @@ protected LayoutElement layoutElement /// public virtual float preferredWidth { get { m_preferredWidth = GetPreferredWidth(); return m_preferredWidth; } } protected float m_preferredWidth; - protected float m_renderedWidth; + protected float m_RenderedWidth; protected bool m_isPreferredWidthDirty; /// @@ -1391,7 +1434,7 @@ protected LayoutElement layoutElement /// public virtual float preferredHeight { get { m_preferredHeight = GetPreferredHeight(); return m_preferredHeight; } } protected float m_preferredHeight; - protected float m_renderedHeight; + protected float m_RenderedHeight; protected bool m_isPreferredHeightDirty; protected bool m_isCalculatingPreferredValues; @@ -1491,11 +1534,11 @@ public SpecialCharacter(TMP_Character character, int materialIndex) protected int m_totalCharacterCount; // Structures used to save the state of the text layout in conjunction with line breaking / word wrapping. - protected static WordWrapState m_SavedWordWrapState = new WordWrapState(); - protected static WordWrapState m_SavedLineState = new WordWrapState(); - protected static WordWrapState m_SavedEllipsisState = new WordWrapState(); - protected static WordWrapState m_SavedLastValidState = new WordWrapState(); - protected static WordWrapState m_SavedSoftLineBreakState = new WordWrapState(); + internal static WordWrapState m_SavedWordWrapState = new WordWrapState(); + internal static WordWrapState m_SavedLineState = new WordWrapState(); + internal static WordWrapState m_SavedEllipsisState = new WordWrapState(); + internal static WordWrapState m_SavedLastValidState = new WordWrapState(); + internal static WordWrapState m_SavedSoftLineBreakState = new WordWrapState(); //internal Stack m_LineBreakCandiateStack = new Stack(); internal static TMP_TextProcessingStack m_EllipsisInsertionCandidateStack = new TMP_TextProcessingStack(8, 8); @@ -1510,6 +1553,7 @@ public SpecialCharacter(TMP_Character character, int materialIndex) protected int m_lastVisibleCharacterOfLine; protected int m_lineNumber; protected int m_lineVisibleCharacterCount; + protected int m_lineVisibleSpaceCount; protected int m_pageNumber; protected float m_PageAscender; protected float m_maxTextAscender; @@ -2215,6 +2259,16 @@ void PopulateTextProcessingArray() writeIndex += 1; readIndex += 5; continue; + case MarkupTag.SHY: + if (writeIndex == m_TextProcessingArray.Length) ResizeInternalArray(ref m_TextProcessingArray); + + m_TextProcessingArray[writeIndex].unicode = 0xAD; + m_TextProcessingArray[writeIndex].stringIndex = readIndex; + m_TextProcessingArray[writeIndex].length = 5; + + writeIndex += 1; + readIndex += 4; + continue; case MarkupTag.STYLE: int openWriteIndex = writeIndex; if (ReplaceOpeningStyleTag(ref m_TextBackingArray, readIndex, out int srcOffset, ref m_TextProcessingArray, ref writeIndex)) @@ -2286,11 +2340,35 @@ void SetTextInternal(string sourceText) m_inputSource = currentInputSource; } + /// + /// This function is the same as using the text property to set the text. + /// + /// String containing the text. + public void SetText(string sourceText) + { + int srcLength = sourceText == null ? 0 : sourceText.Length; + + PopulateTextBackingArray(sourceText, 0, srcLength); + + m_text = sourceText; + + // Set input source + m_inputSource = TextInputSources.TextString; + + PopulateTextProcessingArray(); + + m_havePropertiesChanged = true; + + SetVerticesDirty(); + SetLayoutDirty(); + } + /// /// This function is the same as using the text property to set the text. /// /// String containing the text. /// This optional parameter no longer provides any functionality as this function now simple sets the .text property which is reflected in the Text Input Box. + [Obsolete("Use the SetText(string) function instead.")] public void SetText(string sourceText, bool syncTextInputBox = true) { int srcLength = sourceText == null ? 0 : sourceText.Length; @@ -2799,6 +2877,13 @@ bool ReplaceOpeningStyleTag(ref TextBackingContainer sourceText, int srcIndex, o writeIndex += 1; i += 5; continue; + case MarkupTag.SHY: + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0xAD; + writeIndex += 1; + i += 4; + continue; case MarkupTag.STYLE: if (ReplaceOpeningStyleTag(ref tagDefinition, i, out int offset, ref charBuffer, ref writeIndex)) { @@ -2921,6 +3006,13 @@ bool ReplaceOpeningStyleTag(ref int[] sourceText, int srcIndex, out int srcOffse writeIndex += 1; i += 5; continue; + case MarkupTag.SHY: + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0xAD; + writeIndex += 1; + i += 4; + continue; case MarkupTag.STYLE: if (ReplaceOpeningStyleTag(ref tagDefinition, i, out int offset, ref charBuffer, ref writeIndex)) { @@ -3040,6 +3132,13 @@ void ReplaceClosingStyleTag(ref TextBackingContainer sourceText, int srcIndex, r writeIndex += 1; i += 5; continue; + case MarkupTag.SHY: + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0xAD; + writeIndex += 1; + i += 4; + continue; case MarkupTag.STYLE: if (ReplaceOpeningStyleTag(ref tagDefinition, i, out int offset, ref charBuffer, ref writeIndex)) { @@ -3156,6 +3255,13 @@ void ReplaceClosingStyleTag(ref int[] sourceText, int srcIndex, ref UnicodeChar[ writeIndex += 1; i += 5; continue; + case MarkupTag.SHY: + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0xAD; + writeIndex += 1; + i += 4; + continue; case MarkupTag.STYLE: if (ReplaceOpeningStyleTag(ref tagDefinition, i, out int offset, ref charBuffer, ref writeIndex)) { @@ -3268,6 +3374,13 @@ bool InsertOpeningStyleTag(TMP_Style style, int srcIndex, ref UnicodeChar[] char writeIndex += 1; i += 5; continue; + case MarkupTag.SHY: + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0xAD; + writeIndex += 1; + i += 4; + continue; case MarkupTag.STYLE: if (ReplaceOpeningStyleTag(ref tagDefinition, i, out int offset, ref charBuffer, ref writeIndex)) { @@ -3377,6 +3490,13 @@ void InsertClosingStyleTag(ref UnicodeChar[] charBuffer, ref int writeIndex) writeIndex += 1; i += 5; continue; + case MarkupTag.SHY: + if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer); + + charBuffer[writeIndex].unicode = 0xAD; + writeIndex += 1; + i += 4; + continue; case MarkupTag.STYLE: if (ReplaceOpeningStyleTag(ref tagDefinition, i, out int offset, ref charBuffer, ref writeIndex)) { @@ -3720,7 +3840,7 @@ public Vector2 GetPreferredValues(string text, float width, float height) Vector2 margin = new Vector2(width, height); // CALCULATE PREFERRED WIDTH - float preferredWidth = GetPreferredWidth(margin); + float preferredWidth = GetPreferredWidth(margin, m_TextWrappingMode); // CALCULATE PREFERRED HEIGHT float preferredHeight = GetPreferredHeight(margin); @@ -3755,7 +3875,8 @@ protected float GetPreferredWidth() ParseInputText(); m_AutoSizeIterationCount = 0; - float preferredWidth = CalculatePreferredValues(ref fontSize, margin, false, false).x; + TextWrappingModes wrapMode = m_TextWrappingMode == TextWrappingModes.Normal || m_TextWrappingMode == TextWrappingModes.NoWrap ? TextWrappingModes.NoWrap : TextWrappingModes.PreserveWhitespaceNoWrap; + float preferredWidth = CalculatePreferredValues(ref fontSize, margin, false, wrapMode).x; m_isPreferredWidthDirty = false; @@ -3765,11 +3886,6 @@ protected float GetPreferredWidth() } - /// - /// Method to calculate the preferred width of a text object. - /// - /// - /// float GetPreferredWidth(Vector2 margin) { float fontSize = m_enableAutoSizing ? m_fontSizeMax : m_fontSize; @@ -3780,7 +3896,26 @@ float GetPreferredWidth(Vector2 margin) m_charWidthAdjDelta = 0; m_AutoSizeIterationCount = 0; - float preferredWidth = CalculatePreferredValues(ref fontSize, margin, false, false).x; + TextWrappingModes wrapMode = m_TextWrappingMode == TextWrappingModes.Normal || m_TextWrappingMode == TextWrappingModes.NoWrap ? TextWrappingModes.NoWrap : TextWrappingModes.PreserveWhitespaceNoWrap; + float preferredWidth = CalculatePreferredValues(ref fontSize, margin, false, wrapMode).x; + + //Debug.Log("GetPreferredWidth() Called. Returning width of " + preferredWidth); + + return preferredWidth; + } + + + float GetPreferredWidth(Vector2 margin, TextWrappingModes wrapMode) + { + float fontSize = m_enableAutoSizing ? m_fontSizeMax : m_fontSize; + + // Reset auto sizing point size bounds + m_minFontSize = m_fontSizeMin; + m_maxFontSize = m_fontSizeMax; + m_charWidthAdjDelta = 0; + + m_AutoSizeIterationCount = 0; + float preferredWidth = CalculatePreferredValues(ref fontSize, margin, false, wrapMode).x; //Debug.Log("GetPreferredWidth() Called. Returning width of " + preferredWidth); @@ -3822,7 +3957,7 @@ protected float GetPreferredHeight() while (m_IsAutoSizePointSizeSet == false) { - preferredHeight = CalculatePreferredValues(ref fontSize, margin, m_enableAutoSizing, m_enableWordWrapping).y; + preferredHeight = CalculatePreferredValues(ref fontSize, margin, m_enableAutoSizing, m_TextWrappingMode).y; m_AutoSizeIterationCount += 1; } @@ -3858,7 +3993,7 @@ float GetPreferredHeight(Vector2 margin) while (m_IsAutoSizePointSizeSet == false) { - preferredHeight = CalculatePreferredValues(ref fontSize, margin, m_enableAutoSizing, m_enableWordWrapping).y; + preferredHeight = CalculatePreferredValues(ref fontSize, margin, m_enableAutoSizing, m_TextWrappingMode).y; m_AutoSizeIterationCount += 1; } @@ -3929,7 +4064,7 @@ protected float GetRenderedHeight(bool onlyVisibleCharacters) /// Method to calculate the preferred width and height of the text object. ///
/// - protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 marginSize, bool isTextAutoSizingEnabled, bool isWordWrappingEnabled) + protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 marginSize, bool isTextAutoSizingEnabled, TextWrappingModes textWrapMode) { //Debug.Log("*** CalculatePreferredValues() ***"); // ***** Frame: " + Time.frameCount); @@ -3985,9 +4120,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m m_cSpacing = 0; // Amount of space added between characters as a result of the use of the tag. m_monoSpacing = 0; - //float lineOffsetDelta = 0; m_xAdvance = 0; // Used to track the position of each character. - float maxXAdvance = 0; // Used to determine Preferred Width. tag_LineIndent = 0; // Used for indentation of text. tag_Indent = 0; @@ -4011,15 +4144,12 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m m_marginLeft = 0; m_marginRight = 0; - float lineMarginLeft = 0; - float lineMarginRight = 0; - m_width = -1; float widthOfTextArea = marginWidth + 0.0001f - m_marginLeft - m_marginRight; // Used by Unity's Auto Layout system. - float renderedWidth = 0; - float renderedHeight = 0; + m_RenderedWidth = 0; + m_RenderedHeight = 0; float textWidth = 0; m_isCalculatingPreferredValues = true; @@ -4168,7 +4298,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m // Look up Character Data from Dictionary and cache it. #region Look up Character Data - //float baselineOffset = 0; + float baselineOffset = 0; float elementAscentLine = 0; float elementDescentLine = 0; if (m_textElementType == TMP_TextElementType.Sprite) @@ -4236,7 +4366,6 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m } currentElementScale = adjustedScale * m_fontScaleMultiplier * m_cached_TextElement.scale; - //baselineOffset = m_currentFontAsset.faceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.faceInfo.scale; m_internalCharacterInfo[m_characterCount].elementType = TMP_TextElementType.Character; } @@ -4265,7 +4394,6 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m #region Handle Kerning TMP_GlyphValueRecord glyphAdjustments = new TMP_GlyphValueRecord(); float characterSpacingAdjustment = m_characterSpacing; - m_GlyphHorizontalAdvanceAdjustment = 0; if (m_enableKerning) { TMP_GlyphPairAdjustmentRecord adjustmentPair; @@ -4276,7 +4404,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m uint nextGlyphIndex = m_textInfo.characterInfo[m_characterCount + 1].textElement.m_GlyphIndex; uint key = nextGlyphIndex << 16 | baseGlyphIndex; - if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out adjustmentPair)) + if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair)) { glyphAdjustments = adjustmentPair.m_FirstAdjustmentRecord.m_GlyphValueRecord; characterSpacingAdjustment = (adjustmentPair.m_FeatureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; @@ -4288,17 +4416,72 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m uint previousGlyphIndex = m_textInfo.characterInfo[m_characterCount - 1].textElement.m_GlyphIndex; uint key = baseGlyphIndex << 16 | previousGlyphIndex; - if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out adjustmentPair)) + if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair)) { glyphAdjustments += adjustmentPair.m_SecondAdjustmentRecord.m_GlyphValueRecord; characterSpacingAdjustment = (adjustmentPair.m_FeatureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; } } - m_GlyphHorizontalAdvanceAdjustment = glyphAdjustments.m_XAdvance; + m_internalCharacterInfo[m_characterCount].adjustedHorizontalAdvance = glyphAdjustments.m_XAdvance; } #endregion + // Handle Diacritical Marks + #region Handle Diacritical Marks + bool isBaseGlyph = TMP_TextParsingUtilities.IsBaseGlyph((uint)charCode); + + if (isBaseGlyph) + m_LastBaseGlyphIndex = m_characterCount; + + if (!isBaseGlyph && /* m_MarkToBaseEnabled && */ m_characterCount > 0) + { + Glyph baseGlyph = m_textInfo.characterInfo[m_LastBaseGlyphIndex].textElement.glyph; + uint baseGlyphIndex = baseGlyph.index; + uint markGlyphIndex = m_cached_TextElement.glyphIndex; + uint key = markGlyphIndex << 16 | baseGlyphIndex; + + // TODO: Add Type to glyph make it easier to identify the glyph type. + MarkToBaseAdjustmentRecord glyphAdjustmentRecord; + + if (m_currentFontAsset.fontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.TryGetValue(key, out glyphAdjustmentRecord)) + { + float advanceOffset = (m_internalCharacterInfo[m_LastBaseGlyphIndex].origin - m_xAdvance) / currentElementScale; + + glyphAdjustments.xPlacement = advanceOffset + glyphAdjustmentRecord.baseGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.markPositionAdjustment.xPositionAdjustment; + glyphAdjustments.yPlacement = glyphAdjustmentRecord.baseGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.markPositionAdjustment.yPositionAdjustment; + + characterSpacingAdjustment = 0; + } + } + + if (!isBaseGlyph && /* m_MarkToMarkEnabled && */ m_characterCount > 0) + { + MarkToMarkAdjustmentRecord glyphAdjustmentRecord; + + Glyph baseGlyph = m_textInfo.characterInfo[m_characterCount - 1].textElement.glyph; + uint baseGlyphIndex = baseGlyph.index; + uint markGlyphIndex = m_cached_TextElement.glyphIndex; + uint key = markGlyphIndex << 16 | baseGlyphIndex; + + if (m_currentFontAsset.fontFeatureTable.m_MarkToMarkAdjustmentRecordLookup.TryGetValue(key, out glyphAdjustmentRecord)) + { + float baseMarkOrigin = (m_internalCharacterInfo[m_characterCount - 1].origin - m_xAdvance) / currentElementScale; + float currentBaseline = baselineOffset - m_lineOffset + m_baselineOffset; + float baseMarkBaseline = (m_internalCharacterInfo[m_characterCount - 1].baseLine - currentBaseline) / currentElementScale; + + glyphAdjustments.xPlacement = baseMarkOrigin + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.xPositionAdjustment; + glyphAdjustments.yPlacement = baseMarkBaseline + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.yPositionAdjustment; + + characterSpacingAdjustment = 0; + } + } + + // Adjust relevant text metrics + elementAscentLine += glyphAdjustments.yPlacement; + elementDescentLine += glyphAdjustments.yPlacement; + #endregion + // Initial Implementation for RTL support. #region Handle Right-to-Left @@ -4330,7 +4513,8 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m boldSpacingAdjustment = m_currentFontAsset.boldSpacing; #endregion Handle Style Padding - m_internalCharacterInfo[m_characterCount].baseLine = 0 - m_lineOffset + m_baselineOffset; + m_internalCharacterInfo[m_characterCount].origin = m_xAdvance + glyphAdjustments.xPlacement * currentElementScale; + m_internalCharacterInfo[m_characterCount].baseLine = (baselineOffset - m_lineOffset + m_baselineOffset) + glyphAdjustments.yPlacement * currentElementScale; // Compute text metrics #region Compute Ascender & Descender values @@ -4347,8 +4531,8 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m float adjustedAscender = elementAscender; float adjustedDescender = elementDescender; - bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine; // Max line ascender and descender in line space + bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine; if (isFirstCharacterOfLine || isWhiteSpace == false) { // Special handling for Superscript and Subscript where we use the unadjusted line ascender and descender @@ -4402,7 +4586,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN. #region Handle Visible Characters - if (charCode == 9 || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) + if (charCode == 9 || ((textWrapMode == TextWrappingModes.PreserveWhitespace || textWrapMode == TextWrappingModes.PreserveWhitespaceNoWrap) && isWhiteSpace) || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) { //float marginLeft = m_marginLeft; //float marginRight = m_marginRight; @@ -4423,10 +4607,10 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m // Handling of Horizontal Bounds #region Current Line Horizontal Bounds Check - if (textWidth > widthOfTextArea * (isJustifiedOrFlush ? 1.05f : 1.0f)) + if (isBaseGlyph && textWidth > widthOfTextArea * (isJustifiedOrFlush ? 1.05f : 1.0f)) { // Handle Line Breaking (if still possible) - if (isWordWrappingEnabled && m_characterCount != m_firstCharacterOfLine) // && isFirstWord == false) + if (textWrapMode != TextWrappingModes.NoWrap && textWrapMode != TextWrappingModes.PreserveWhitespaceNoWrap && m_characterCount != m_firstCharacterOfLine) { // Restore state to previous safe line breaking i = RestoreWordWrappingState(ref internalWordWrapState); @@ -4518,14 +4702,6 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m m_firstCharacterOfLine = m_characterCount; m_lineVisibleCharacterCount = 0; - // Compute Preferred Width & Height - renderedWidth += m_xAdvance; - - if (isWordWrappingEnabled) - renderedHeight = m_maxTextAscender - m_ElementDescender; - else - renderedHeight = Mathf.Max(renderedHeight, lineAscender - lineDescender); - // Store the state of the line before starting on the new line. SaveWordWrappingState(ref internalLineState, i, m_characterCount - 1); @@ -4557,8 +4733,9 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m } #endregion - lineMarginLeft = m_marginLeft; - lineMarginRight = m_marginRight; + // Compute Preferred Width & Height + m_RenderedWidth = Mathf.Max(m_RenderedWidth, textWidth + m_marginLeft + m_marginRight); + m_RenderedHeight = m_maxTextAscender - m_ElementDescender; } #endregion Handle Visible Characters @@ -4609,8 +4786,6 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m #region Carriage Return if (charCode == 13) { - maxXAdvance = Mathf.Max(maxXAdvance, renderedWidth + m_xAdvance); - renderedWidth = 0; m_xAdvance = 0 + tag_Indent; } #endregion Carriage Return @@ -4636,17 +4811,6 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m // Update maxDescender and maxVisibleDescender m_ElementDescender = m_ElementDescender < lineDescender ? m_ElementDescender : lineDescender; - // Store PreferredWidth paying attention to linefeed and last character of text. - if (m_characterCount == totalCharacterCount - 1) - renderedWidth = Mathf.Max(maxXAdvance, renderedWidth + textWidth + lineMarginLeft + lineMarginRight); - else - { - maxXAdvance = Mathf.Max(maxXAdvance, renderedWidth + textWidth + lineMarginLeft + lineMarginRight); - renderedWidth = 0; - } - - renderedHeight = m_maxTextAscender - m_ElementDescender; - // Add new line if not last lines or character. if (charCode == 10 || charCode == 11 || charCode == 0x2D || charCode == 0x2028 || charCode == 0x2029) { @@ -4692,7 +4856,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m // Save State of Mesh Creation for handling of Word Wrapping #region Save Word Wrapping State - if (isWordWrappingEnabled || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis) + if ((textWrapMode != TextWrappingModes.NoWrap && textWrapMode != TextWrappingModes.PreserveWhitespaceNoWrap) || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis) { if ((isWhiteSpace || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && !m_isNonBreakingSpace && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060) { @@ -4789,19 +4953,19 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m m_isCalculatingPreferredValues = false; // Adjust Preferred Width and Height to account for Margins. - renderedWidth += m_margin.x > 0 ? m_margin.x : 0; - renderedWidth += m_margin.z > 0 ? m_margin.z : 0; + m_RenderedWidth += m_margin.x > 0 ? m_margin.x : 0; + m_RenderedWidth += m_margin.z > 0 ? m_margin.z : 0; - renderedHeight += m_margin.y > 0 ? m_margin.y : 0; - renderedHeight += m_margin.w > 0 ? m_margin.w : 0; + m_RenderedHeight += m_margin.y > 0 ? m_margin.y : 0; + m_RenderedHeight += m_margin.w > 0 ? m_margin.w : 0; // Round Preferred Values to nearest 5/100. - renderedWidth = (int)(renderedWidth * 100 + 1f) / 100f; - renderedHeight = (int)(renderedHeight * 100 + 1f) / 100f; + m_RenderedWidth = (int)(m_RenderedWidth * 100 + 1f) / 100f; + m_RenderedHeight = (int)(m_RenderedHeight * 100 + 1f) / 100f; //Debug.Log("Preferred Values: (" + renderedWidth + ", " + renderedHeight + ") with Recursive count of " + m_recursiveCount); - return new Vector2(renderedWidth, renderedHeight); + return new Vector2(m_RenderedWidth, m_RenderedHeight); } @@ -5011,7 +5175,7 @@ public virtual void ComputeMarginSize() { } //} - protected void InsertNewLine(int i, float baseScale, float currentElementScale, float currentEmScale, float glyphAdjustment, float boldSpacingAdjustment, float characterSpacingAdjustment, float width, float lineGap, ref bool isMaxVisibleDescenderSet, ref float maxVisibleDescender) + internal void InsertNewLine(int i, float baseScale, float currentElementScale, float currentEmScale, float boldSpacingAdjustment, float characterSpacingAdjustment, float width, float lineGap, ref bool isMaxVisibleDescenderSet, ref float maxVisibleDescender) { k_InsertNewLineMarker.Begin(); @@ -5044,14 +5208,16 @@ protected void InsertNewLine(int i, float baseScale, float currentElementScale, m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1; m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount; + m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = m_lineVisibleSpaceCount; m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender); m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender); m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x; m_textInfo.lineInfo[m_lineNumber].width = width; - float maxAdvanceOffset = (glyphAdjustment * currentElementScale + (m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale - m_cSpacing) * (1 - m_charWidthAdjDelta); + float glyphAdjustment = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].adjustedHorizontalAdvance; + float maxAdvanceOffset = (glyphAdjustment * currentElementScale + (m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta); float adjustedHorizontalAdvance = m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance + (m_isRightToLeft ? maxAdvanceOffset : - maxAdvanceOffset); - m_textInfo.characterInfo[lastCharacterIndex].xAdvance = adjustedHorizontalAdvance; + m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance = adjustedHorizontalAdvance; m_textInfo.lineInfo[m_lineNumber].baseline = 0 - m_lineOffset; m_textInfo.lineInfo[m_lineNumber].ascender = lineAscender; @@ -5060,6 +5226,7 @@ protected void InsertNewLine(int i, float baseScale, float currentElementScale, m_firstCharacterOfLine = m_characterCount; // Store first character of the next line. m_lineVisibleCharacterCount = 0; + m_lineVisibleSpaceCount = 0; // Store the state of the line before starting on the new line. SaveWordWrappingState(ref m_SavedLineState, i, m_characterCount - 1); @@ -5098,7 +5265,7 @@ protected void InsertNewLine(int i, float baseScale, float currentElementScale, /// /// /// - protected void SaveWordWrappingState(ref WordWrapState state, int index, int count) + internal void SaveWordWrappingState(ref WordWrapState state, int index, int count) { // Multi Font & Material support related state.currentFontAsset = m_currentFontAsset; @@ -5109,6 +5276,7 @@ protected void SaveWordWrappingState(ref WordWrapState state, int index, int cou state.previous_WordBreak = index; state.total_CharacterCount = count; state.visible_CharacterCount = m_lineVisibleCharacterCount; + state.visibleSpaceCount = m_lineVisibleSpaceCount; //state.visible_CharacterCount = m_visibleCharacterCount; //state.visible_SpriteCount = m_visibleSpriteCount; state.visible_LinkCount = m_textInfo.linkCount; @@ -5119,7 +5287,6 @@ protected void SaveWordWrappingState(ref WordWrapState state, int index, int cou state.fontStyle = m_FontStyleInternal; state.italicAngle = m_ItalicAngle; - //state.maxFontScale = m_maxFontScale; state.fontScaleMultiplier = m_fontScaleMultiplier; state.currentFontSize = m_currentFontSize; @@ -5134,13 +5301,15 @@ protected void SaveWordWrappingState(ref WordWrapState state, int index, int cou state.preferredWidth = m_preferredWidth; state.preferredHeight = m_preferredHeight; + state.renderedWidth = m_RenderedWidth; + state.renderedHeight = m_RenderedHeight; state.meshExtents = m_meshExtents; state.lineNumber = m_lineNumber; state.lineOffset = m_lineOffset; state.baselineOffset = m_baselineOffset; state.isDrivenLineSpacing = m_IsDrivenLineSpacing; - state.glyphHorizontalAdvanceAdjustment = m_GlyphHorizontalAdvanceAdjustment; + state.lastBaseGlyphIndex = m_LastBaseGlyphIndex; state.cSpace = m_cSpacing; state.mSpace = m_monoSpacing; @@ -5152,6 +5321,7 @@ protected void SaveWordWrappingState(ref WordWrapState state, int index, int cou state.vertexColor = m_htmlColor; state.underlineColor = m_underlineColor; state.strikethroughColor = m_strikethroughColor; + state.highlightState = m_HighlightState; state.isNonBreakingSpace = m_isNonBreakingSpace; state.tagNoParsing = tag_NoParsing; @@ -5187,7 +5357,7 @@ protected void SaveWordWrappingState(ref WordWrapState state, int index, int cou ///
/// /// - protected int RestoreWordWrappingState(ref WordWrapState state) + internal int RestoreWordWrappingState(ref WordWrapState state) { int index = state.previous_WordBreak; @@ -5199,6 +5369,7 @@ protected int RestoreWordWrappingState(ref WordWrapState state) m_characterCount = state.total_CharacterCount + 1; m_lineVisibleCharacterCount = state.visible_CharacterCount; + m_lineVisibleSpaceCount = state.visibleSpaceCount; //m_visibleCharacterCount = state.visible_CharacterCount; //m_visibleSpriteCount = state.visible_SpriteCount; m_textInfo.linkCount = state.visible_LinkCount; @@ -5210,7 +5381,6 @@ protected int RestoreWordWrappingState(ref WordWrapState state) m_FontStyleInternal = state.fontStyle; m_ItalicAngle = state.italicAngle; m_fontScaleMultiplier = state.fontScaleMultiplier; - //m_maxFontScale = state.maxFontScale; m_currentFontSize = state.currentFontSize; m_xAdvance = state.xAdvance; @@ -5224,13 +5394,15 @@ protected int RestoreWordWrappingState(ref WordWrapState state) m_preferredWidth = state.preferredWidth; m_preferredHeight = state.preferredHeight; + m_RenderedWidth = state.renderedWidth; + m_RenderedHeight = state.renderedHeight; m_meshExtents = state.meshExtents; m_lineNumber = state.lineNumber; m_lineOffset = state.lineOffset; m_baselineOffset = state.baselineOffset; m_IsDrivenLineSpacing = state.isDrivenLineSpacing; - m_GlyphHorizontalAdvanceAdjustment = state.glyphHorizontalAdvanceAdjustment; + m_LastBaseGlyphIndex = state.lastBaseGlyphIndex; m_cSpacing = state.cSpace; m_monoSpacing = state.mSpace; @@ -5242,6 +5414,7 @@ protected int RestoreWordWrappingState(ref WordWrapState state) m_htmlColor = state.vertexColor; m_underlineColor = state.underlineColor; m_strikethroughColor = state.strikethroughColor; + m_HighlightState = state.highlightState; m_isNonBreakingSpace = state.isNonBreakingSpace; tag_NoParsing = state.tagNoParsing; @@ -5543,10 +5716,10 @@ protected virtual void FillCharacterVertexBuffers(int i, int index_X4) // setup Vertex Colors - m_textInfo.meshInfo[materialIndex].colors32[0 + index_X4] = characterInfoArray[i].vertex_BL.color; - m_textInfo.meshInfo[materialIndex].colors32[1 + index_X4] = characterInfoArray[i].vertex_TL.color; - m_textInfo.meshInfo[materialIndex].colors32[2 + index_X4] = characterInfoArray[i].vertex_TR.color; - m_textInfo.meshInfo[materialIndex].colors32[3 + index_X4] = characterInfoArray[i].vertex_BR.color; + m_textInfo.meshInfo[materialIndex].colors32[0 + index_X4] = m_ConvertToLinearSpace ? characterInfoArray[i].vertex_BL.color.GammaToLinear() : characterInfoArray[i].vertex_BL.color; + m_textInfo.meshInfo[materialIndex].colors32[1 + index_X4] = m_ConvertToLinearSpace ? characterInfoArray[i].vertex_TL.color.GammaToLinear() : characterInfoArray[i].vertex_TL.color; + m_textInfo.meshInfo[materialIndex].colors32[2 + index_X4] = m_ConvertToLinearSpace ? characterInfoArray[i].vertex_TR.color.GammaToLinear() : characterInfoArray[i].vertex_TR.color; + m_textInfo.meshInfo[materialIndex].colors32[3 + index_X4] = m_ConvertToLinearSpace ? characterInfoArray[i].vertex_BR.color.GammaToLinear() : characterInfoArray[i].vertex_BR.color; m_textInfo.meshInfo[materialIndex].vertexCount = index_X4 + 4; } @@ -5681,10 +5854,10 @@ protected virtual void FillSpriteVertexBuffers(int i, int index_X4) // setup Vertex Colors - m_textInfo.meshInfo[materialIndex].colors32[0 + index_X4] = characterInfoArray[i].vertex_BL.color; - m_textInfo.meshInfo[materialIndex].colors32[1 + index_X4] = characterInfoArray[i].vertex_TL.color; - m_textInfo.meshInfo[materialIndex].colors32[2 + index_X4] = characterInfoArray[i].vertex_TR.color; - m_textInfo.meshInfo[materialIndex].colors32[3 + index_X4] = characterInfoArray[i].vertex_BR.color; + m_textInfo.meshInfo[materialIndex].colors32[0 + index_X4] = m_ConvertToLinearSpace ? characterInfoArray[i].vertex_BL.color.GammaToLinear() : characterInfoArray[i].vertex_BL.color; + m_textInfo.meshInfo[materialIndex].colors32[1 + index_X4] = m_ConvertToLinearSpace ? characterInfoArray[i].vertex_TL.color.GammaToLinear() : characterInfoArray[i].vertex_TL.color; + m_textInfo.meshInfo[materialIndex].colors32[2 + index_X4] = m_ConvertToLinearSpace ? characterInfoArray[i].vertex_TR.color.GammaToLinear() : characterInfoArray[i].vertex_TR.color; + m_textInfo.meshInfo[materialIndex].colors32[3 + index_X4] = m_ConvertToLinearSpace ? characterInfoArray[i].vertex_BR.color.GammaToLinear() : characterInfoArray[i].vertex_BR.color; m_textInfo.meshInfo[materialIndex].vertexCount = index_X4 + 4; } @@ -5767,20 +5940,22 @@ protected virtual void DrawUnderlineMesh(Vector3 start, Vector3 end, ref int ind // UNDERLINE UV0 #region HANDLE UV0 - Vector2[] uvs0 = m_textInfo.meshInfo[underlineMaterialIndex].uvs0; + Vector4[] uvs0 = m_textInfo.meshInfo[underlineMaterialIndex].uvs0; int atlasWidth = m_Underline.fontAsset.atlasWidth; int atlasHeight = m_Underline.fontAsset.atlasHeight; + float xScale = Mathf.Abs(sdfScale); + // Calculate UV required to setup the 3 Quads for the Underline. - Vector2 uv0 = new Vector2((underlineGlyphRect.x - startPadding) / atlasWidth, (underlineGlyphRect.y - m_padding) / atlasHeight); // bottom left - Vector2 uv1 = new Vector2(uv0.x, (underlineGlyphRect.y + underlineGlyphRect.height + m_padding) / atlasHeight); // top left - Vector2 uv2 = new Vector2((underlineGlyphRect.x - startPadding + (float)underlineGlyphRect.width / 2) / atlasWidth, uv1.y); // Mid Top Left - Vector2 uv3 = new Vector2(uv2.x, uv0.y); // Mid Bottom Left - Vector2 uv4 = new Vector2((underlineGlyphRect.x + endPadding + (float)underlineGlyphRect.width / 2) / atlasWidth, uv1.y); // Mid Top Right - Vector2 uv5 = new Vector2(uv4.x, uv0.y); // Mid Bottom right - Vector2 uv6 = new Vector2((underlineGlyphRect.x + endPadding + underlineGlyphRect.width) / atlasWidth, uv1.y); // End Part - Bottom Right - Vector2 uv7 = new Vector2(uv6.x, uv0.y); // End Part - Top Right + Vector4 uv0 = new Vector4((underlineGlyphRect.x - startPadding) / atlasWidth, (underlineGlyphRect.y - m_padding) / atlasHeight, 0, xScale); // bottom left + Vector4 uv1 = new Vector4(uv0.x, (underlineGlyphRect.y + underlineGlyphRect.height + m_padding) / atlasHeight, 0, xScale); // top left + Vector4 uv2 = new Vector4((underlineGlyphRect.x - startPadding + (float)underlineGlyphRect.width / 2) / atlasWidth, uv1.y, 0, xScale); // Mid Top Left + Vector4 uv3 = new Vector4(uv2.x, uv0.y, 0, xScale); // Mid Bottom Left + Vector4 uv4 = new Vector4((underlineGlyphRect.x + endPadding + (float)underlineGlyphRect.width / 2) / atlasWidth, uv1.y, 0, xScale); // Mid Top Right + Vector4 uv5 = new Vector4(uv4.x, uv0.y, 0, xScale); // Mid Bottom right + Vector4 uv6 = new Vector4((underlineGlyphRect.x + endPadding + underlineGlyphRect.width) / atlasWidth, uv1.y, 0, xScale); // End Part - Bottom Right + Vector4 uv7 = new Vector4(uv6.x, uv0.y, 0, xScale); // End Part - Top Right // Left Part of the Underline uvs0[0 + index] = uv0; // BL @@ -5789,10 +5964,10 @@ protected virtual void DrawUnderlineMesh(Vector3 start, Vector3 end, ref int ind uvs0[3 + index] = uv3; // BR // Middle Part of the Underline - uvs0[4 + index] = new Vector2(uv2.x - uv2.x * 0.001f, uv0.y); - uvs0[5 + index] = new Vector2(uv2.x - uv2.x * 0.001f, uv1.y); - uvs0[6 + index] = new Vector2(uv2.x + uv2.x * 0.001f, uv1.y); - uvs0[7 + index] = new Vector2(uv2.x + uv2.x * 0.001f, uv0.y); + uvs0[4 + index] = new Vector4(uv2.x - uv2.x * 0.001f, uv0.y, 0, xScale); + uvs0[5 + index] = new Vector4(uv2.x - uv2.x * 0.001f, uv1.y, 0, xScale); + uvs0[6 + index] = new Vector4(uv2.x + uv2.x * 0.001f, uv1.y, 0, xScale); + uvs0[7 + index] = new Vector4(uv2.x + uv2.x * 0.001f, uv0.y, 0, xScale); // Right Part of the Underline uvs0[8 + index] = uv5; @@ -5807,30 +5982,27 @@ protected virtual void DrawUnderlineMesh(Vector3 start, Vector3 end, ref int ind float min_UvX = 0; float max_UvX = (vertices[index + 2].x - start.x) / (end.x - start.x); - //Calculate the xScale or how much the UV's are getting stretched on the X axis for the middle section of the underline. - float xScale = Mathf.Abs(sdfScale); - Vector2[] uvs2 = m_textInfo.meshInfo[underlineMaterialIndex].uvs2; - uvs2[0 + index] = PackUV(0, 0, xScale); - uvs2[1 + index] = PackUV(0, 1, xScale); - uvs2[2 + index] = PackUV(max_UvX, 1, xScale); - uvs2[3 + index] = PackUV(max_UvX, 0, xScale); + uvs2[0 + index] = new Vector2 (0, 0); + uvs2[1 + index] = new Vector2(0, 1); + uvs2[2 + index] = new Vector2(max_UvX, 1); + uvs2[3 + index] = new Vector2(max_UvX, 0); min_UvX = (vertices[index + 4].x - start.x) / (end.x - start.x); max_UvX = (vertices[index + 6].x - start.x) / (end.x - start.x); - uvs2[4 + index] = PackUV(min_UvX, 0, xScale); - uvs2[5 + index] = PackUV(min_UvX, 1, xScale); - uvs2[6 + index] = PackUV(max_UvX, 1, xScale); - uvs2[7 + index] = PackUV(max_UvX, 0, xScale); + uvs2[4 + index] = new Vector2(min_UvX, 0); + uvs2[5 + index] = new Vector2(min_UvX, 1); + uvs2[6 + index] = new Vector2(max_UvX, 1); + uvs2[7 + index] = new Vector2(max_UvX, 0); min_UvX = (vertices[index + 8].x - start.x) / (end.x - start.x); - uvs2[8 + index] = PackUV(min_UvX, 0, xScale); - uvs2[9 + index] = PackUV(min_UvX, 1, xScale); - uvs2[10 + index] = PackUV(1, 1, xScale); - uvs2[11 + index] = PackUV(1, 0, xScale); + uvs2[8 + index] = new Vector2(min_UvX, 0); + uvs2[9 + index] = new Vector2(min_UvX, 1); + uvs2[10 + index] = new Vector2(1, 1); + uvs2[11 + index] = new Vector2(1, 0); #endregion // UNDERLINE VERTEX COLORS @@ -5898,20 +6070,21 @@ protected virtual void DrawTextHighlight(Vector3 start, Vector3 end, ref int ind // UNDERLINE UV0 #region HANDLE UV0 - Vector2[] uvs0 = m_textInfo.meshInfo[underlineMaterialIndex].uvs0; + Vector4[] uvs0 = m_textInfo.meshInfo[underlineMaterialIndex].uvs0; int atlasWidth = m_Underline.fontAsset.atlasWidth; int atlasHeight = m_Underline.fontAsset.atlasHeight; GlyphRect glyphRect = m_Underline.character.glyph.glyphRect; // Calculate UV - Vector2 uv0 = new Vector2(((float)glyphRect.x + glyphRect.width / 2) / atlasWidth, (glyphRect.y + (float)glyphRect.height / 2) / atlasHeight); // bottom left + Vector2 uvGlyphCenter = new Vector2((glyphRect.x + (float)glyphRect.width / 2) / atlasWidth, (glyphRect.y + (float)glyphRect.height / 2) / atlasHeight); + Vector2 uvTexelSize = new Vector2(1.0f / atlasWidth, 1.0f / atlasHeight); // UVs for the Quad - uvs0[0 + index] = uv0; // BL - uvs0[1 + index] = uv0; // TL - uvs0[2 + index] = uv0; // TR - uvs0[3 + index] = uv0; // BR + uvs0[0 + index] = uvGlyphCenter - uvTexelSize; // BL + uvs0[1 + index] = uvGlyphCenter + new Vector2(-uvTexelSize.x, uvTexelSize.y); // TL + uvs0[2 + index] = uvGlyphCenter + uvTexelSize; // TR + uvs0[3 + index] = uvGlyphCenter + new Vector2(uvTexelSize.x, -uvTexelSize.y); // BR #endregion // HIGHLIGHT UV2 @@ -5925,7 +6098,7 @@ protected virtual void DrawTextHighlight(Vector3 start, Vector3 end, ref int ind #endregion // HIGHLIGHT VERTEX COLORS - #region + #region HIGHLIGHT VERTEX COLORS // Alpha is the lower of the vertex color or tag color alpha used. highlightColor.a = m_fontColor32.a < highlightColor.a ? m_fontColor32.a : highlightColor.a; @@ -5968,7 +6141,7 @@ protected void LoadDefaultSettings() } - m_enableWordWrapping = TMP_Settings.enableWordWrapping; + m_TextWrappingMode = TMP_Settings.textWrappingMode; m_enableKerning = TMP_Settings.enableKerning; m_enableExtraPadding = TMP_Settings.enableExtraPadding; m_tintAllSprites = TMP_Settings.enableTintAllSprites; @@ -6149,7 +6322,7 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F if (character != null) { // Add character to font asset lookup cache - //fontAsset.AddCharacterToLookupCache(unicode, character); + fontAsset.AddCharacterToLookupCache(unicode, character); return character; } @@ -6167,7 +6340,7 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F m_currentMaterial = m_materialReferences[0].material; // Add character to font asset lookup cache - //fontAsset.AddCharacterToLookupCache(unicode, character); + fontAsset.AddCharacterToLookupCache(unicode, character); return character; } @@ -6179,7 +6352,7 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F if (character != null) { // Add character to font asset lookup cache - //fontAsset.AddCharacterToLookupCache(unicode, character); + fontAsset.AddCharacterToLookupCache(unicode, character); return character; } @@ -6201,7 +6374,7 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F if (character != null) { // Add character to font asset lookup cache - //fontAsset.AddCharacterToLookupCache(unicode, character); + fontAsset.AddCharacterToLookupCache(unicode, character); return character; } @@ -6213,7 +6386,7 @@ internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, F if (character != null) { // Add character to font asset lookup cache - //fontAsset.AddCharacterToLookupCache(unicode, character); + fontAsset.AddCharacterToLookupCache(unicode, character); return character; } @@ -6312,6 +6485,12 @@ internal void ReleaseLinkedTextComponent(TMP_Text targetTextComponent) targetTextComponent.parentLinkedComponent = null; } + protected void DoMissingGlyphCallback(int unicode, int stringIndex, TMP_FontAsset fontAsset) + { + // Event to allow users to modify the content of the text info before the text is rendered. + OnMissingCharacter?.Invoke(unicode, stringIndex, m_text, fontAsset, this); + } + /// /// Function to pack scale information in the UV2 Channel. @@ -6824,6 +7003,12 @@ protected float ConvertToFloat(char[] chars, int startIndex, int length, out int return value; } + void ClearMarkupTagAttributes() + { + int length = m_xmlAttribute.Length; + for (int i = 0; i < length; i++) + m_xmlAttribute[i] = new RichTextTagAttribute(); + } /// /// Function to identify and validate the rich tag. Returns the position of the > if the tag was valid. @@ -6838,18 +7023,9 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn byte attributeFlag = 0; int attributeIndex = 0; - m_xmlAttribute[attributeIndex].nameHashCode = 0; - m_xmlAttribute[attributeIndex].valueHashCode = 0; - m_xmlAttribute[attributeIndex].valueStartIndex = 0; - m_xmlAttribute[attributeIndex].valueLength = 0; - TagValueType tagValueType = m_xmlAttribute[attributeIndex].valueType = TagValueType.None; - TagUnitType tagUnitType = m_xmlAttribute[attributeIndex].unitType = TagUnitType.Pixels; - - // Clear attribute name hash codes - m_xmlAttribute[1].nameHashCode = 0; - m_xmlAttribute[2].nameHashCode = 0; - m_xmlAttribute[3].nameHashCode = 0; - m_xmlAttribute[4].nameHashCode = 0; + ClearMarkupTagAttributes(); + TagValueType tagValueType = TagValueType.None; + TagUnitType tagUnitType = TagUnitType.Pixels; endIndex = startIndex; bool isTagSet = false; @@ -6900,7 +7076,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn tagUnitType = TagUnitType.Pixels; tagValueType = m_xmlAttribute[attributeIndex].valueType = TagValueType.StringValue; m_xmlAttribute[attributeIndex].valueStartIndex = tagCharCount - 1; - m_xmlAttribute[attributeIndex].valueHashCode = (m_xmlAttribute[attributeIndex].valueHashCode << 5) + m_xmlAttribute[attributeIndex].valueHashCode ^ unicode; + m_xmlAttribute[attributeIndex].valueHashCode = (m_xmlAttribute[attributeIndex].valueHashCode << 5) + m_xmlAttribute[attributeIndex].valueHashCode ^ TMP_TextUtilities.ToUpperFast((char)unicode); m_xmlAttribute[attributeIndex].valueLength += 1; } } @@ -6966,7 +7142,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn // Compute HashCode value for the named tag. if (unicode != '"') { - m_xmlAttribute[attributeIndex].valueHashCode = (m_xmlAttribute[attributeIndex].valueHashCode << 5) + m_xmlAttribute[attributeIndex].valueHashCode ^ unicode; + m_xmlAttribute[attributeIndex].valueHashCode = (m_xmlAttribute[attributeIndex].valueHashCode << 5) + m_xmlAttribute[attributeIndex].valueHashCode ^ TMP_TextUtilities.ToUpperFast((char)unicode); m_xmlAttribute[attributeIndex].valueLength += 1; } else @@ -7010,7 +7186,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn } if (attributeFlag == 0) - m_xmlAttribute[attributeIndex].nameHashCode = (m_xmlAttribute[attributeIndex].nameHashCode << 3) - m_xmlAttribute[attributeIndex].nameHashCode + unicode; + m_xmlAttribute[attributeIndex].nameHashCode = (m_xmlAttribute[attributeIndex].nameHashCode << 5) + m_xmlAttribute[attributeIndex].nameHashCode ^ TMP_TextUtilities.ToUpperFast((char)unicode); if (attributeFlag == 2 && unicode == ' ') attributeFlag = 0; @@ -7029,9 +7205,10 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn #region Rich Text Tag Processing #if !RICH_TEXT_ENABLED // Special handling of the no parsing tag tag - if (tag_NoParsing && (m_xmlAttribute[0].nameHashCode != 53822163 && m_xmlAttribute[0].nameHashCode != 49429939)) + if (tag_NoParsing && (m_xmlAttribute[0].nameHashCode != (int)MarkupTag.SLASH_NO_PARSE)) return false; - else if (m_xmlAttribute[0].nameHashCode == 53822163 || m_xmlAttribute[0].nameHashCode == 49429939) + + if (m_xmlAttribute[0].nameHashCode == (int)MarkupTag.SLASH_NO_PARSE) { tag_NoParsing = false; return true; @@ -7070,17 +7247,15 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn float value = 0; float fontScale; - switch (m_xmlAttribute[0].nameHashCode) + switch ((MarkupTag)m_xmlAttribute[0].nameHashCode) { - case 98: // - case 66: // + case MarkupTag.BOLD: m_FontStyleInternal |= FontStyles.Bold; m_fontStyleStack.Add(FontStyles.Bold); m_FontWeightInternal = FontWeight.Bold; return true; - case 427: // - case 395: // + case MarkupTag.SLASH_BOLD: if ((m_fontStyle & FontStyles.Bold) != FontStyles.Bold) { if (m_fontStyleStack.Remove(FontStyles.Bold) == 0) @@ -7090,12 +7265,11 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn } } return true; - case 105: // - case 73: // + case MarkupTag.ITALIC: m_FontStyleInternal |= FontStyles.Italic; m_fontStyleStack.Add(FontStyles.Italic); - if (m_xmlAttribute[1].nameHashCode == 276531 || m_xmlAttribute[1].nameHashCode == 186899) + if (m_xmlAttribute[1].nameHashCode == (int)MarkupTag.ANGLE) { m_ItalicAngle = (int)ConvertToFloat(m_htmlTag, m_xmlAttribute[1].valueStartIndex, m_xmlAttribute[1].valueLength); @@ -7108,8 +7282,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_ItalicAngleStack.Add(m_ItalicAngle); return true; - case 434: // - case 402: // + case MarkupTag.SLASH_ITALIC: if ((m_fontStyle & FontStyles.Italic) != FontStyles.Italic) { m_ItalicAngle = m_ItalicAngleStack.Remove(); @@ -7118,12 +7291,11 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_FontStyleInternal &= ~FontStyles.Italic; } return true; - case 115: // - case 83: // + case MarkupTag.STRIKETHROUGH: m_FontStyleInternal |= FontStyles.Strikethrough; m_fontStyleStack.Add(FontStyles.Strikethrough); - if (m_xmlAttribute[1].nameHashCode == 281955 || m_xmlAttribute[1].nameHashCode == 192323) + if (m_xmlAttribute[1].nameHashCode == (int)MarkupTag.COLOR) { m_strikethroughColor = HexCharsToColor(m_htmlTag, m_xmlAttribute[1].valueStartIndex, m_xmlAttribute[1].valueLength); m_strikethroughColor.a = m_htmlColor.a < m_strikethroughColor.a ? (byte)(m_htmlColor.a) : (byte)(m_strikethroughColor .a); @@ -7134,8 +7306,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_strikethroughColorStack.Add(m_strikethroughColor); return true; - case 444: // - case 412: // + case MarkupTag.SLASH_STRIKETHROUGH: if ((m_fontStyle & FontStyles.Strikethrough) != FontStyles.Strikethrough) { if (m_fontStyleStack.Remove(FontStyles.Strikethrough) == 0) @@ -7144,15 +7315,14 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_strikethroughColor = m_strikethroughColorStack.Remove(); return true; - case 117: // - case 85: // + case MarkupTag.UNDERLINE: m_FontStyleInternal |= FontStyles.Underline; m_fontStyleStack.Add(FontStyles.Underline); - if (m_xmlAttribute[1].nameHashCode == 281955 || m_xmlAttribute[1].nameHashCode == 192323) + if (m_xmlAttribute[1].nameHashCode == (int)MarkupTag.COLOR) { m_underlineColor = HexCharsToColor(m_htmlTag, m_xmlAttribute[1].valueStartIndex, m_xmlAttribute[1].valueLength); - m_underlineColor.a = m_htmlColor.a < m_underlineColor.a ? (byte)(m_htmlColor.a) : (byte)(m_underlineColor.a); + m_underlineColor.a = m_htmlColor.a < m_underlineColor.a ? (m_htmlColor.a) : (m_underlineColor.a); } else m_underlineColor = m_htmlColor; @@ -7160,20 +7330,16 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_underlineColorStack.Add(m_underlineColor); return true; - case 446: // - case 414: // + case MarkupTag.SLASH_UNDERLINE: if ((m_fontStyle & FontStyles.Underline) != FontStyles.Underline) { - m_underlineColor = m_underlineColorStack.Remove(); - if (m_fontStyleStack.Remove(FontStyles.Underline) == 0) m_FontStyleInternal &= ~FontStyles.Underline; } m_underlineColor = m_underlineColorStack.Remove(); return true; - case 43045: // - case 30245: // + case MarkupTag.MARK: m_FontStyleInternal |= FontStyles.Highlight; m_fontStyleStack.Add(FontStyles.Highlight); @@ -7183,24 +7349,21 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn // Handle Mark Tag and potential attributes for (int i = 0; i < m_xmlAttribute.Length && m_xmlAttribute[i].nameHashCode != 0; i++) { - int nameHashCode = m_xmlAttribute[i].nameHashCode; - - switch (nameHashCode) + switch ((MarkupTag)m_xmlAttribute[i].nameHashCode) { // Mark tag - case 43045: - case 30245: + case MarkupTag.MARK: if (m_xmlAttribute[i].valueType == TagValueType.ColorValue) highlightColor = HexCharsToColor(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); break; // Color attribute - case 281955: + case MarkupTag.COLOR: highlightColor = HexCharsToColor(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength); break; // Padding attribute - case 15087385: + case MarkupTag.PADDING: int paramCount = GetAttributeParameters(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength, ref m_attributeParameterValues); if (paramCount != 4) return false; @@ -7212,22 +7375,21 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn highlightColor.a = m_htmlColor.a < highlightColor.a ? (byte)(m_htmlColor.a) : (byte)(highlightColor.a); - HighlightState state = new HighlightState(highlightColor, highlightPadding); - m_HighlightStateStack.Push(state); + m_HighlightState = new HighlightState(highlightColor, highlightPadding); + m_HighlightStateStack.Push(m_HighlightState); return true; - case 155892: // - case 143092: // + case MarkupTag.SLASH_MARK: if ((m_fontStyle & FontStyles.Highlight) != FontStyles.Highlight) { m_HighlightStateStack.Remove(); + m_HighlightState = m_HighlightStateStack.current; if (m_fontStyleStack.Remove(FontStyles.Highlight) == 0) m_FontStyleInternal &= ~FontStyles.Highlight; } return true; - case 6552: // - case 4728: // + case MarkupTag.SUBSCRIPT: m_fontScaleMultiplier *= m_currentFontAsset.faceInfo.subscriptSize > 0 ? m_currentFontAsset.faceInfo.subscriptSize : 1; m_baselineOffsetStack.Push(m_baselineOffset); fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f)); @@ -7236,8 +7398,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_fontStyleStack.Add(FontStyles.Subscript); m_FontStyleInternal |= FontStyles.Subscript; return true; - case 22673: // - case 20849: // + case MarkupTag.SLASH_SUBSCRIPT: if ((m_FontStyleInternal & FontStyles.Subscript) == FontStyles.Subscript) { if (m_fontScaleMultiplier < 1) @@ -7250,8 +7411,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_FontStyleInternal &= ~FontStyles.Subscript; } return true; - case 6566: // - case 4742: // + case MarkupTag.SUPERSCRIPT: m_fontScaleMultiplier *= m_currentFontAsset.faceInfo.superscriptSize > 0 ? m_currentFontAsset.faceInfo.superscriptSize : 1; m_baselineOffsetStack.Push(m_baselineOffset); fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f)); @@ -7260,8 +7420,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_fontStyleStack.Add(FontStyles.Superscript); m_FontStyleInternal |= FontStyles.Superscript; return true; - case 22687: // - case 20863: // + case MarkupTag.SLASH_SUPERSCRIPT: if ((m_FontStyleInternal & FontStyles.Superscript) == FontStyles.Superscript) { if (m_fontScaleMultiplier < 1) @@ -7274,8 +7433,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_FontStyleInternal &= ~FontStyles.Superscript; } return true; - case -330774850: // - case 2012149182: // + case MarkupTag.FONT_WEIGHT: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -7315,8 +7473,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_FontWeightStack.Add(m_FontWeightInternal); return true; - case -1885698441: // - case 457225591: // + case MarkupTag.SLASH_FONT_WEIGHT: m_FontWeightStack.Remove(); if (m_FontStyleInternal == FontStyles.Bold) @@ -7325,8 +7482,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_FontWeightInternal = m_FontWeightStack.Peek(); return true; - case 6380: // - case 4556: // + case MarkupTag.POSITION: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -7348,12 +7504,10 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn return true; } return false; - case 22501: // - case 20677: // + case MarkupTag.SLASH_POSITION: m_isIgnoringAlignment = false; return true; - case 16034505: // - case 11642281: // + case MarkupTag.VERTICAL_OFFSET: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -7372,12 +7526,10 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn return false; } return false; - case 54741026: // - case 50348802: // + case MarkupTag.SLASH_VERTICAL_OFFSET: m_baselineOffset = 0; return true; - case 43991: // - case 31191: // + case MarkupTag.PAGE: // This tag only works when Overflow - Page mode is used. if (m_overflowMode == TextOverflowModes.Page) { @@ -7387,21 +7539,13 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_isNewPage = true; } return true; - //
tag is now handled inline where it is replaced by a linefeed or \n. - //case 544: //
- //case 800: //
- // m_forceLineBreak = true; - // return true; - case 43969: // - case 31169: // + case MarkupTag.NO_BREAK: m_isNonBreakingSpace = true; return true; - case 156816: // - case 144016: // + case MarkupTag.SLASH_NO_BREAK: m_isNonBreakingSpace = false; return true; - case 45545: // - case 32745: // + case MarkupTag.SIZE: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -7438,18 +7582,16 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn return true; } return false; - case 158392: // - case 145592: // + case MarkupTag.SLASH_SIZE: m_currentFontSize = m_sizeStack.Remove(); return true; - case 41311: // - case 28511: // + case MarkupTag.FONT: int fontHashCode = m_xmlAttribute[0].valueHashCode; int materialAttributeHashCode = m_xmlAttribute[1].nameHashCode; int materialHashCode = m_xmlAttribute[1].valueHashCode; // Special handling for or - if (fontHashCode == 764638571 || fontHashCode == 523367755) + if (fontHashCode == (int)MarkupTag.DEFAULT) { m_currentFontAsset = m_materialReferences[0].fontAsset; m_currentMaterial = m_materialReferences[0].material; @@ -7499,7 +7641,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_materialReferenceStack.Add(m_materialReferences[m_currentMaterialIndex]); } - else if (materialAttributeHashCode == 103415287 || materialAttributeHashCode == 72669687) // using material attribute + else if (materialAttributeHashCode == (int)MarkupTag.MATERIAL) // using material attribute { if (MaterialReferenceManager.TryGetMaterial(materialHashCode, out tempMaterial)) { @@ -7533,8 +7675,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_currentFontAsset = tempFont; return true; - case 154158: // - case 141358: //
+ case MarkupTag.SLASH_FONT: { MaterialReference materialReference = m_materialReferenceStack.Remove(); @@ -7544,12 +7685,11 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn return true; } - case 103415287: // - case 72669687: // + case MarkupTag.MATERIAL: materialHashCode = m_xmlAttribute[0].valueHashCode; // Special handling for or - if (materialHashCode == 764638571 || materialHashCode == 523367755) + if (materialHashCode == (int)MarkupTag.DEFAULT) { // Check if material font atlas texture matches that of the current font asset. //if (m_currentFontAsset.atlas.GetInstanceID() != m_currentMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) return false; @@ -7596,8 +7736,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_materialReferenceStack.Add(m_materialReferences[m_currentMaterialIndex]); } return true; - case 374360934: // - case 343615334: // + case MarkupTag.SLASH_MATERIAL: { //if (m_currentMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() != m_materialReferenceStack.PreviousItem().material.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) // return false; @@ -7609,8 +7748,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn return true; } - case 320078: // - case 230446: // + case MarkupTag.SPACE: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -7629,19 +7767,17 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn return false; } return false; - case 276254: // - case 186622: // + case MarkupTag.ALPHA: if (m_xmlAttribute[0].valueLength != 3) return false; m_htmlColor.a = (byte)(HexToInt(m_htmlTag[7]) * 16 + HexToInt(m_htmlTag[8])); return true; - case 1750458: // + case MarkupTag.A: return false; - case 426: // + case MarkupTag.SLASH_A: return true; - case 43066: // - case 30266: // + case MarkupTag.LINK: if (m_isParsingText && !m_isCalculatingPreferredValues) { int index = m_textInfo.linkCount; @@ -7658,8 +7794,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_textInfo.linkInfo[index].SetLinkID(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); } return true; - case 155913: // - case 143113: // + case MarkupTag.SLASH_LINK: if (m_isParsingText && !m_isCalculatingPreferredValues) { if (m_textInfo.linkCount < m_textInfo.linkInfo.Length) @@ -7670,38 +7805,35 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn } } return true; - case 275917: // - case 186285: // - switch (m_xmlAttribute[0].valueHashCode) + case MarkupTag.ALIGN: + switch ((MarkupTag)m_xmlAttribute[0].valueHashCode) { - case 3774683: // + case MarkupTag.LEFT: // m_lineJustification = HorizontalAlignmentOptions.Left; m_lineJustificationStack.Add(m_lineJustification); return true; - case 136703040: // + case MarkupTag.RIGHT: // m_lineJustification = HorizontalAlignmentOptions.Right; m_lineJustificationStack.Add(m_lineJustification); return true; - case -458210101: // + case MarkupTag.CENTER: // m_lineJustification = HorizontalAlignmentOptions.Center; m_lineJustificationStack.Add(m_lineJustification); return true; - case -523808257: // + case MarkupTag.JUSTIFIED: // m_lineJustification = HorizontalAlignmentOptions.Justified; m_lineJustificationStack.Add(m_lineJustification); return true; - case 122383428: // + case MarkupTag.FLUSH: // m_lineJustification = HorizontalAlignmentOptions.Flush; m_lineJustificationStack.Add(m_lineJustification); return true; } return false; - case 1065846: // - case 976214: // + case MarkupTag.SLASH_ALIGN: m_lineJustification = m_lineJustificationStack.Remove(); return true; - case 327550: // - case 237918: // + case MarkupTag.WIDTH: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -7720,8 +7852,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn break; } return true; - case 1117479: // - case 1027847: // + case MarkupTag.SLASH_WIDTH: m_width = -1; return true; // STYLE tag is now handled inline and replaced by its definition. @@ -7763,8 +7894,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn // ValidateHtmlTag(style.styleClosingTagArray, i + 1, out i); // } // return true; - case 281955: // or - case 192323: // + case MarkupTag.COLOR: // 3 Hex (short hand) if (m_htmlTag[6] == 35 && tagCharCount == 10) { @@ -7797,7 +7927,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn // switch (m_xmlAttribute[0].valueHashCode) { - case 125395: // + case (int)MarkupTag.RED: // m_htmlColor = Color.red; m_colorStack.Add(m_htmlColor); return true; @@ -7805,7 +7935,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_htmlColor = new Color32(173, 216, 230, 255); m_colorStack.Add(m_htmlColor); return true; - case 3573310: // + case (int)MarkupTag.BLUE: // m_htmlColor = Color.blue; m_colorStack.Add(m_htmlColor); return true; @@ -7813,35 +7943,34 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_htmlColor = new Color32(128, 128, 128, 255); m_colorStack.Add(m_htmlColor); return true; - case 117905991: // + case (int)MarkupTag.BLACK: // m_htmlColor = Color.black; m_colorStack.Add(m_htmlColor); return true; - case 121463835: // + case (int)MarkupTag.GREEN: // m_htmlColor = Color.green; m_colorStack.Add(m_htmlColor); return true; - case 140357351: // + case (int)MarkupTag.WHITE: // m_htmlColor = Color.white; m_colorStack.Add(m_htmlColor); return true; - case 26556144: // + case (int)MarkupTag.ORANGE: // m_htmlColor = new Color32(255, 128, 0, 255); m_colorStack.Add(m_htmlColor); return true; - case -36881330: // + case (int)MarkupTag.PURPLE: // m_htmlColor = new Color32(160, 32, 240, 255); m_colorStack.Add(m_htmlColor); return true; - case 554054276: // + case (int)MarkupTag.YELLOW: // m_htmlColor = Color.yellow; m_colorStack.Add(m_htmlColor); return true; } return false; - case 100149144: // - case 69403544: // + case MarkupTag.GRADIENT: int gradientPresetHashCode = m_xmlAttribute[0].valueHashCode; TMP_ColorGradient tempColorGradientPreset; @@ -7873,10 +8002,9 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn // Get attribute name int nameHashCode = m_xmlAttribute[i].nameHashCode; - switch (nameHashCode) + switch ((MarkupTag)nameHashCode) { - case 45819: // tint - case 33019: // TINT + case MarkupTag.TINT: m_colorGradientPresetIsTinted = ConvertToFloat(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength) != 0; break; } @@ -7888,13 +8016,11 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn return true; - case 371094791: // - case 340349191: // + case MarkupTag.SLASH_GRADIENT: m_colorGradientPreset = m_colorGradientStack.Remove(); return true; - case 1983971: // - case 1356515: // + case MarkupTag.CHARACTER_SPACE: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -7912,8 +8038,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn return false; } return true; - case 7513474: // - case 6886018: // + case MarkupTag.SLASH_CHARACTER_SPACE: if (!m_isParsingText) return true; // Adjust xAdvance to remove extra space from last character. @@ -7924,8 +8049,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn } m_cSpacing = 0; return true; - case 2152041: // - case 1524585: // + case MarkupTag.MONOSPACE: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -7943,18 +8067,15 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn return false; } return true; - case 7681544: // - case 7054088: // + case MarkupTag.SLASH_MONOSPACE: m_monoSpacing = 0; return true; - case 280416: // + case MarkupTag.CLASS: return false; - case 1071884: // - case 982252: // + case MarkupTag.SLASH_COLOR: m_htmlColor = m_colorStack.Remove(); return true; - case 2068980: // - case 1441524: // + case MarkupTag.INDENT: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -7976,13 +8097,11 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_xAdvance = tag_Indent; return true; - case 7598483: // - case 6971027: // + case MarkupTag.SLASH_INDENT: tag_Indent = m_indentStack.Remove(); //m_xAdvance = tag_Indent; return true; - case 1109386397: // - case -842656867: // + case MarkupTag.LINE_INDENT: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -8003,12 +8122,10 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_xAdvance += tag_LineIndent; return true; - case -445537194: // - case 1897386838: // + case MarkupTag.SLASH_LINE_INDENT: tag_LineIndent = 0; return true; - case 2246877: // - case 1619421: // + case MarkupTag.SPRITE: int spriteAssetHashCode = m_xmlAttribute[0].valueHashCode; TMP_SpriteAsset tempSpriteAsset; m_spriteIndex = -1; @@ -8091,17 +8208,15 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn int nameHashCode = m_xmlAttribute[i].nameHashCode; int index = 0; - switch (nameHashCode) + switch ((MarkupTag)nameHashCode) { - case 43347: // - case 30547: // + case MarkupTag.NAME: m_currentSpriteAsset = TMP_SpriteAsset.SearchForSpriteByHashCode(m_currentSpriteAsset, m_xmlAttribute[i].valueHashCode, true, out index); if (index == -1) return false; m_spriteIndex = index; break; - case 295562: // - case 205930: // + case MarkupTag.INDEX: index = (int)ConvertToFloat(m_htmlTag, m_xmlAttribute[1].valueStartIndex, m_xmlAttribute[1].valueLength); // Reject tag if value is invalid. @@ -8112,16 +8227,13 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_spriteIndex = index; break; - case 45819: // tint - case 33019: // TINT + case MarkupTag.TINT: m_tintSprite = ConvertToFloat(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength) != 0; break; - case 281955: // color=#FF00FF80 - case 192323: // COLOR + case MarkupTag.COLOR: m_spriteColor = HexCharsToColor(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength); break; - case 39505: // anim="0,16,12" start, end, fps - case 26705: // ANIM + case MarkupTag.ANIM: //Debug.Log("Start: " + m_xmlAttribute[i].valueStartIndex + " Length: " + m_xmlAttribute[i].valueLength); int paramCount = GetAttributeParameters(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength, ref m_attributeParameterValues); if (paramCount != 3) return false; @@ -8143,7 +8255,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn // break; default: - if (nameHashCode != 2246877 && nameHashCode != 1619421) + if (nameHashCode != (int)MarkupTag.SPRITE) return false; break; } @@ -8156,51 +8268,42 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_textElementType = TMP_TextElementType.Sprite; return true; - case 730022849: // - case 514803617: // + case MarkupTag.LOWERCASE: m_FontStyleInternal |= FontStyles.LowerCase; m_fontStyleStack.Add(FontStyles.LowerCase); return true; - case -1668324918: // - case -1883544150: // + case MarkupTag.SLASH_LOWERCASE: if ((m_fontStyle & FontStyles.LowerCase) != FontStyles.LowerCase) { if (m_fontStyleStack.Remove(FontStyles.LowerCase) == 0) m_FontStyleInternal &= ~FontStyles.LowerCase; } return true; - case 13526026: // - case 9133802: // - case 781906058: // - case 566686826: // + case MarkupTag.ALLCAPS: + case MarkupTag.UPPERCASE: m_FontStyleInternal |= FontStyles.UpperCase; m_fontStyleStack.Add(FontStyles.UpperCase); return true; - case 52232547: // - case 47840323: // - case -1616441709: // - case -1831660941: // + case MarkupTag.SLASH_ALLCAPS: + case MarkupTag.SLASH_UPPERCASE: if ((m_fontStyle & FontStyles.UpperCase) != FontStyles.UpperCase) { if (m_fontStyleStack.Remove(FontStyles.UpperCase) == 0) m_FontStyleInternal &= ~FontStyles.UpperCase; } return true; - case 766244328: // - case 551025096: // + case MarkupTag.SMALLCAPS: m_FontStyleInternal |= FontStyles.SmallCaps; m_fontStyleStack.Add(FontStyles.SmallCaps); return true; - case -1632103439: // - case -1847322671: // + case MarkupTag.SLASH_SMALLCAPS: if ((m_fontStyle & FontStyles.SmallCaps) != FontStyles.SmallCaps) { if (m_fontStyleStack.Remove(FontStyles.SmallCaps) == 0) m_FontStyleInternal &= ~FontStyles.SmallCaps; } return true; - case 2109854: // - case 1482398: // + case MarkupTag.MARGIN: // Check value type switch (m_xmlAttribute[0].valueType) { @@ -8233,9 +8336,9 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn // Get attribute name int nameHashCode = m_xmlAttribute[i].nameHashCode; - switch (nameHashCode) + switch ((MarkupTag)nameHashCode) { - case 42823: // + case MarkupTag.LEFT: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength); // px // Reject tag if value is invalid. @@ -8256,7 +8359,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_marginLeft = m_marginLeft >= 0 ? m_marginLeft : 0; break; - case 315620: // + case MarkupTag.RIGHT: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength); // px // Reject tag if value is invalid. @@ -8282,13 +8385,11 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn } return false; - case 7639357: // - case 7011901: // + case MarkupTag.SLASH_MARGIN: m_marginLeft = 0; m_marginRight = 0; return true; - case 1100728678: // - case -855002522: // + case MarkupTag.MARGIN_LEFT: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // px // Reject tag if value is invalid. @@ -8308,8 +8409,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn } m_marginLeft = m_marginLeft >= 0 ? m_marginLeft : 0; return true; - case -884817987: // - case -1690034531: // + case MarkupTag.MARGIN_RIGHT: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // px // Reject tag if value is invalid. @@ -8329,8 +8429,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn } m_marginRight = m_marginRight >= 0 ? m_marginRight : 0; return true; - case 1109349752: // - case -842693512: // + case MarkupTag.LINE_HEIGHT: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -8350,16 +8449,13 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn break; } return true; - case -445573839: // - case 1897350193: // + case MarkupTag.SLASH_LINE_HEIGHT: m_lineHeight = TMP_Math.FLOAT_UNSET; return true; - case 15115642: // - case 10723418: // + case MarkupTag.NO_PARSE: tag_NoParsing = true; return true; - case 1913798: // - case 1286342: // + case MarkupTag.ACTION: int actionID = m_xmlAttribute[0].valueHashCode; if (m_isParsingText) @@ -8375,8 +8471,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn // TMP_Action action = TMP_Action.GetAction(m_xmlAttribute[0].valueHashCode); //} return true; - case 7443301: // - case 6815845: // + case MarkupTag.SLASH_ACTION: if (m_isParsingText) { Debug.Log("Action ID: [" + m_actionStack.CurrentItem() + "] Last character index: " + (m_characterCount - 1)); @@ -8384,8 +8479,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_actionStack.Remove(); return true; - case 315682: // - case 226050: // + case MarkupTag.SCALE: value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // Reject tag if value is invalid. @@ -8395,12 +8489,10 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_isFXMatrixSet = true; return true; - case 1105611: // - case 1015979: // + case MarkupTag.SLASH_SCALE: m_isFXMatrixSet = false; return true; - case 2227963: // - case 1600507: // + case MarkupTag.ROTATE: // TODO: Add ability to use Random Rotation value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); @@ -8412,12 +8504,10 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn m_isFXMatrixSet = true; return true; - case 7757466: // - case 7130010: // + case MarkupTag.SLASH_ROTATE: m_isFXMatrixSet = false; return true; - case 317446: // - case 227814: //
+ case MarkupTag.TABLE: //switch (m_xmlAttribute[1].nameHashCode) //{ // case 327550: // width @@ -8441,24 +8531,18 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn // break; //} return false; - case 1107375: //
- case 1017743: // + case MarkupTag.SLASH_TABLE: return true; - case 926: // - case 670: // + case MarkupTag.TR: return true; - case 3229: // - case 2973: // + case MarkupTag.SLASH_TR: return true; - case 916: // - case 660: // + case MarkupTag.TH: // Set style to bold and center alignment return true; - case 3219: // - case 2963: // + case MarkupTag.SLASH_TH: return true; - case 912: // - case 656: // + case MarkupTag.TD: // Style options //for (int i = 1; i < m_xmlAttribute.Length && m_xmlAttribute[i].nameHashCode != 0; i++) //{ @@ -8501,8 +8585,7 @@ internal bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIn //} return false; - case 3215: // - case 2959: // + case MarkupTag.SLASH_TD: return false; } } diff --git a/Scripts/Runtime/TMP_TextInfo.cs b/Scripts/Runtime/TMP_TextInfo.cs index 168234b..6428dad 100644 --- a/Scripts/Runtime/TMP_TextInfo.cs +++ b/Scripts/Runtime/TMP_TextInfo.cs @@ -80,7 +80,7 @@ public TMP_TextInfo(TMP_Text textComponent) /// /// Function to clear the counters of the text object. /// - public void Clear() + internal void Clear() { characterCount = 0; spaceCount = 0; @@ -169,10 +169,10 @@ public void ClearUnusedVertices(MaterialReference[] materials) /// /// Function to clear and initialize the lineInfo array. /// - public void ClearLineInfo() + internal void ClearLineInfo() { if (this.lineInfo == null) - this.lineInfo = new TMP_LineInfo[2]; + this.lineInfo = new TMP_LineInfo[1]; int length = this.lineInfo.Length; @@ -182,19 +182,21 @@ public void ClearLineInfo() this.lineInfo[i].spaceCount = 0; this.lineInfo[i].wordCount = 0; this.lineInfo[i].controlCharacterCount = 0; - this.lineInfo[i].width = 0; + + this.lineInfo[i].visibleCharacterCount = 0; + this.lineInfo[i].visibleSpaceCount = 0; this.lineInfo[i].ascender = k_InfinityVectorNegative.x; + this.lineInfo[i].baseline = 0; this.lineInfo[i].descender = k_InfinityVectorPositive.x; + this.lineInfo[i].maxAdvance = 0; this.lineInfo[i].marginLeft = 0; this.lineInfo[i].marginRight = 0; this.lineInfo[i].lineExtents.min = k_InfinityVectorPositive; this.lineInfo[i].lineExtents.max = k_InfinityVectorNegative; - - this.lineInfo[i].maxAdvance = 0; - //this.lineInfo[i].maxScale = 0; + this.lineInfo[i].width = 0; } } @@ -232,7 +234,7 @@ public TMP_MeshInfo[] CopyMeshInfoVertexData() int length = meshInfo[i].vertices.Length; m_CachedMeshInfo[i].vertices = new Vector3[length]; - m_CachedMeshInfo[i].uvs0 = new Vector2[length]; + m_CachedMeshInfo[i].uvs0 = new Vector4[length]; m_CachedMeshInfo[i].uvs2 = new Vector2[length]; m_CachedMeshInfo[i].colors32 = new Color32[length]; @@ -249,7 +251,7 @@ public TMP_MeshInfo[] CopyMeshInfoVertexData() if (m_CachedMeshInfo[i].vertices.Length != length) { m_CachedMeshInfo[i].vertices = new Vector3[length]; - m_CachedMeshInfo[i].uvs0 = new Vector2[length]; + m_CachedMeshInfo[i].uvs0 = new Vector4[length]; m_CachedMeshInfo[i].uvs2 = new Vector2[length]; m_CachedMeshInfo[i].colors32 = new Color32[length]; diff --git a/Scripts/Runtime/TMP_TextParsingUtilities.cs b/Scripts/Runtime/TMP_TextParsingUtilities.cs index 8f7cc19..65da8ee 100644 --- a/Scripts/Runtime/TMP_TextParsingUtilities.cs +++ b/Scripts/Runtime/TMP_TextParsingUtilities.cs @@ -142,5 +142,26 @@ internal static uint ConvertToUTF32(uint highSurrogate, uint lowSurrogate) { return ((highSurrogate - CodePoint.HIGH_SURROGATE_START) * 0x400) + ((lowSurrogate - CodePoint.LOW_SURROGATE_START) + CodePoint.UNICODE_PLANE01_START); } + + /// + /// + /// + /// + /// + internal static bool IsDiacriticalMark(uint c) + { + return c >= 0x300 && c <= 0x36F || c >= 0x1AB0 && c <= 0x1AFF || c >= 0x1DC0 && c <= 0x1DFF || c >= 0x20D0 && c <= 0x20FF || c >= 0xFE20 && c <= 0xFE2F; + } + + internal static bool IsBaseGlyph(uint c) + { + return !(c >= 0x300 && c <= 0x36F || c >= 0x1AB0 && c <= 0x1AFF || c >= 0x1DC0 && c <= 0x1DFF || c >= 0x20D0 && c <= 0x20FF || c >= 0xFE20 && c <= 0xFE2F || + // Thai + c == 0xE31 || c >= 0xE34 && c <= 0xE3A || c >= 0xE47 && c <= 0xE4E || + // Hebrew + c >= 0x591 && c <= 0x5BD || c == 0x5BF || c >= 0x5C1 && c <= 0x5C2 || c >= 0x5C4 && c <= 0x5C5 || c == 0x5C7 + ); + } + } } diff --git a/Scripts/Runtime/TMP_TextProcessingStack.cs b/Scripts/Runtime/TMP_TextProcessingStack.cs index d4a12b7..4fce85d 100644 --- a/Scripts/Runtime/TMP_TextProcessingStack.cs +++ b/Scripts/Runtime/TMP_TextProcessingStack.cs @@ -277,6 +277,7 @@ public void SetDefault(T item) itemStack[0] = item; index = 1; + m_Count = 1; } @@ -301,9 +302,11 @@ public void Add(T item) public T Remove() { index -= 1; + m_Count -= 1; if (index <= 0) { + m_Count = 0; index = 1; return itemStack[0]; diff --git a/Scripts/Runtime/TMP_TextUtilities.cs b/Scripts/Runtime/TMP_TextUtilities.cs index 2aa82bb..7445f17 100644 --- a/Scripts/Runtime/TMP_TextUtilities.cs +++ b/Scripts/Runtime/TMP_TextUtilities.cs @@ -2212,6 +2212,9 @@ internal static uint ToUpperASCIIFast(uint c) /// public static int GetHashCode(string s) { + if (string.IsNullOrEmpty(s)) + return 0; + int hashCode = 0; for (int i = 0; i < s.Length; i++) @@ -2248,6 +2251,21 @@ public static uint GetSimpleHashCodeLowercase(string s) return hashCode; } + /// + /// Function which returns a simple hash code from a string converted to uppercase. + /// + /// The string from which to compute the hash code. + /// The computed hash code. + public static uint GetHashCodeCaseInSensitive(string s) + { + uint hashCode = 0; + + for (int i = 0; i < s.Length; i++) + hashCode = (hashCode << 5) + hashCode ^ ToUpperFast(s[i]); + + return hashCode; + } + /// /// Function to convert Hex to Int @@ -2302,5 +2320,100 @@ public static int StringHexToInt(string s) return value; } + static readonly uint[] crc32Table = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; + + /// + /// + /// + /// + internal static uint CRCBegin() + { + return 0xffffffff; + } + + /// + /// + /// + /// + /// + internal static uint CRCDone(uint crc) + { + return crc ^ 0xffffffff; + } + + /// + /// + /// + /// + /// + /// + internal static uint CRCFeed(uint crc, int value) + { + return (crc >> 8) ^ crc32Table[(crc & 0xFF) ^ value]; + } + + private static byte[] m_bytes = new byte[4]; + + internal static uint CRCFeed(uint crc, float value) + { + unsafe + { + int val = *(int*)&value; + + fixed (byte* b = m_bytes) + { + *((int*)b) = val; + } + + for (int i = 0; i < 4; i++) + { + crc = (crc >> 8) ^ crc32Table[(crc & 0xFF) ^ m_bytes[i]]; + } + + return crc; + } + } + + internal static uint CRCFeed(uint crc, Color value) + { + crc = CRCFeed(crc, value.r); + crc = CRCFeed(crc, value.g); + crc = CRCFeed(crc, value.b); + crc = CRCFeed(crc, value.a); + + return crc; + } } } diff --git a/Scripts/Runtime/TMPro_ExtensionMethods.cs b/Scripts/Runtime/TMPro_ExtensionMethods.cs index a6eee86..7478453 100644 --- a/Scripts/Runtime/TMPro_ExtensionMethods.cs +++ b/Scripts/Runtime/TMPro_ExtensionMethods.cs @@ -142,6 +142,27 @@ public static Color32 Tint(this Color32 c1, float tint) return new Color32(r, g, b, a); } + internal static Color32 GammaToLinear(this Color32 c) + { + return new Color32(GammaToLinear(c.r), GammaToLinear(c.g), GammaToLinear(c.b), c.a); + } + + static byte GammaToLinear(byte value) + { + float v = value / 255f; + + if (v <= 0.04045f) + return (byte)(v / 12.92f * 255f); + + if (v < 1.0f) + return (byte)(Mathf.Pow((v + 0.055f) / 1.055f, 2.4f) * 255); + + if (v == 1.0f) + return 255; + + return (byte)(Mathf.Pow(v, 2.2f) * 255); + } + public static Color MinAlpha(this Color c1, Color c2) { float a = c1.a < c2.a ? c1.a : c2.a; diff --git a/Scripts/Runtime/TMPro_MeshUtilities.cs b/Scripts/Runtime/TMPro_MeshUtilities.cs index 86a6590..ba3b0fe 100644 --- a/Scripts/Runtime/TMPro_MeshUtilities.cs +++ b/Scripts/Runtime/TMPro_MeshUtilities.cs @@ -343,11 +343,12 @@ public override string ToString() // Structure used for Word Wrapping which tracks the state of execution when the last space or carriage return character was encountered. - public struct WordWrapState + internal struct WordWrapState { public int previous_WordBreak; public int total_CharacterCount; public int visible_CharacterCount; + public int visibleSpaceCount; public int visible_SpriteCount; public int visible_LinkCount; public int firstCharacterIndex; @@ -371,7 +372,9 @@ public struct WordWrapState public float xAdvance; public float preferredWidth; public float preferredHeight; - //public float maxFontScale; + public float renderedWidth; + public float renderedHeight; + public float previousLineScale; public int wordCount; @@ -383,7 +386,7 @@ public struct WordWrapState public float baselineOffset; public float lineOffset; public bool isDrivenLineSpacing; - public float glyphHorizontalAdvanceAdjustment; + public int lastBaseGlyphIndex; public float cSpace; public float mSpace; @@ -394,7 +397,7 @@ public struct WordWrapState public Color32 vertexColor; public Color32 underlineColor; public Color32 strikethroughColor; - public Color32 highlightColor; + public HighlightState highlightState; public TMP_FontStyleStack basicStyleStack; public TMP_TextProcessingStack italicAngleStack; public TMP_TextProcessingStack colorStack; @@ -428,15 +431,14 @@ public struct WordWrapState /// /// Structure used to store retrieve the name and hashcode of the font and material /// - public struct TagAttribute + internal struct TagAttribute { public int startIndex; public int length; public int hashCode; } - - public struct RichTextTagAttribute + internal struct RichTextTagAttribute { public int nameHashCode; public int valueHashCode; diff --git a/Scripts/Runtime/TMPro_Private.cs b/Scripts/Runtime/TMPro_Private.cs index e47fed0..0b344ce 100644 --- a/Scripts/Runtime/TMPro_Private.cs +++ b/Scripts/Runtime/TMPro_Private.cs @@ -136,19 +136,6 @@ protected override void Awake() m_cached_TextElement = new TMP_Character(); m_isFirstAllocation = true; - // Check to make sure Sub Text Objects are tracked correctly in the event a Prefab is used. - TMP_SubMesh[] subTextObjects = GetComponentsInChildren(); - if (subTextObjects.Length > 0) - { - int subTextObjectCount = subTextObjects.Length; - - if (subTextObjectCount + 1 > m_subTextObjects.Length) - Array.Resize(ref m_subTextObjects, subTextObjectCount + 1); - - for (int i = 0; i < subTextObjectCount; i++) - m_subTextObjects[i + 1] = subTextObjects[i]; - } - // Set flags to ensure our text is parsed and redrawn. m_havePropertiesChanged = true; @@ -351,7 +338,10 @@ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) Debug.LogWarning("No Material was assigned to " + name + ". " + m_fontAsset.material.name + " was assigned.", this); } else + { Debug.LogWarning("No Font Asset assigned to " + name + ". Please assign a Font Asset.", this); + return; + } } // if (m_fontAsset.atlasTexture != null && m_fontAsset.atlasTexture.GetInstanceID() != m_renderer.sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) @@ -361,7 +351,7 @@ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) // Debug.LogWarning("Font Asset Atlas doesn't match the Atlas in the newly assigned material. Select a matching material or a different font asset.", this); // } - if (m_renderer.sharedMaterial != m_sharedMaterial) // || m_renderer.sharedMaterials.Contains(mat)) + if (m_renderer.sharedMaterial != m_sharedMaterial) { //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED Called on Target ID: " + GetInstanceID() + ". Previous Material:" + m_sharedMaterial + " New Material:" + m_renderer.sharedMaterial); // on Object ID:" + GetInstanceID() + ". m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial.name); m_sharedMaterial = m_renderer.sharedMaterial; @@ -383,15 +373,14 @@ void ON_FONT_PROPERTY_CHANGED(bool isChanged, Object fontAsset) { //Debug.Log("ON_FONT_PROPERTY_CHANGED event received. Target is [" + font.name + "]"); - if (MaterialReference.Contains(m_materialReferences, (TMP_FontAsset)fontAsset)) + // TODO: Optimize so we don't update all text objects when font asset properties are changed. + //if (MaterialReference.Contains(m_materialReferences, (TMP_FontAsset)fontAsset)) { - //Debug.Log("ON_FONT_PROPERTY_CHANGED event received."); m_havePropertiesChanged = true; UpdateMeshPadding(); - SetMaterialDirty(); - SetVerticesDirty(); + SetAllDirty(); } } @@ -521,11 +510,11 @@ protected override void LoadFontAsset() m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4); // Check if we are using the SDF Surface Shader - if (m_sharedMaterial.passCount == 1) + /*if (m_sharedMaterial.FindPass("ShadowCaster") == -1) { m_renderer.receiveShadows = false; m_renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; - } + }*/ } @@ -1154,6 +1143,8 @@ internal override int SetArraySizes(UnicodeChar[] unicodeChars) // Replace missing glyph by the Square (9633) glyph or possibly the Space (32) glyph. if (character == null) { + DoMissingGlyphCallback(unicode, unicodeChars[i].stringIndex, m_currentFontAsset); + // Save the original unicode character int srcGlyph = unicode; @@ -1428,7 +1419,6 @@ public override void ComputeMarginSize() /// protected override void OnDidApplyAnimationProperties() { - //Debug.Log("*** OnDidApplyAnimationProperties() ***"); m_havePropertiesChanged = true; isMaskUpdateRequired = true; @@ -1475,22 +1465,22 @@ internal override void InternalUpdate() { float lossyScaleY = m_rectTransform.lossyScale.y; - // Ignore very small lossy scale changes as their effect on SDF Scale would not be visually noticeable. - // Do not update SDF Scale if the text is null or empty - if (Mathf.Abs(lossyScaleY - m_previousLossyScaleY) > 0.0001f && m_TextProcessingArray[0].unicode != 0) + if (m_previousLossyScaleY != 0 && m_TextProcessingArray[0].unicode != 0) { float scaleDelta = lossyScaleY / m_previousLossyScaleY; - UpdateSDFScale(scaleDelta); - - m_previousLossyScaleY = lossyScaleY; + // Only update SDF Scale when lossy scale has changed by more than 20% + if (scaleDelta < 0.8f || scaleDelta > 1.25f) + { + UpdateSDFScale(scaleDelta); + m_previousLossyScaleY = lossyScaleY; + } } } // Added to handle legacy animation mode. if (m_isUsingLegacyAnimationComponent) { - //if (m_havePropertiesChanged) m_havePropertiesChanged = true; OnPreRenderObject(); } @@ -1712,6 +1702,7 @@ protected virtual void GenerateTextMesh() m_startOfLineAscender = 0; m_startOfLineDescender = 0; m_lineVisibleCharacterCount = 0; + m_lineVisibleSpaceCount = 0; bool isStartOfNewLine = true; m_IsDrivenLineSpacing = false; m_firstOverflowCharacterIndex = -1; @@ -2017,7 +2008,7 @@ protected virtual void GenerateTextMesh() m_textInfo.characterInfo[m_characterCount].color = m_htmlColor; m_textInfo.characterInfo[m_characterCount].underlineColor = m_underlineColor; m_textInfo.characterInfo[m_characterCount].strikethroughColor = m_strikethroughColor; - m_textInfo.characterInfo[m_characterCount].highlightState = m_HighlightStateStack.current; + m_textInfo.characterInfo[m_characterCount].highlightState = m_HighlightState; m_textInfo.characterInfo[m_characterCount].style = m_FontStyleInternal; // Cache glyph metrics @@ -2030,7 +2021,6 @@ protected virtual void GenerateTextMesh() #region Handle Kerning TMP_GlyphValueRecord glyphAdjustments = new TMP_GlyphValueRecord(); float characterSpacingAdjustment = m_characterSpacing; - m_GlyphHorizontalAdvanceAdjustment = 0; if (m_enableKerning) { k_HandleGPOSFeaturesMarker.Begin(); @@ -2043,7 +2033,7 @@ protected virtual void GenerateTextMesh() uint nextGlyphIndex = m_textInfo.characterInfo[m_characterCount + 1].textElement.m_GlyphIndex; uint key = nextGlyphIndex << 16 | baseGlyphIndex; - if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out adjustmentPair)) + if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair)) { glyphAdjustments = adjustmentPair.m_FirstAdjustmentRecord.m_GlyphValueRecord; characterSpacingAdjustment = (adjustmentPair.m_FeatureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; @@ -2055,16 +2045,73 @@ protected virtual void GenerateTextMesh() uint previousGlyphIndex = m_textInfo.characterInfo[m_characterCount - 1].textElement.m_GlyphIndex; uint key = baseGlyphIndex << 16 | previousGlyphIndex; - if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out adjustmentPair)) + if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair)) { glyphAdjustments += adjustmentPair.m_SecondAdjustmentRecord.m_GlyphValueRecord; characterSpacingAdjustment = (adjustmentPair.m_FeatureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; } } - m_GlyphHorizontalAdvanceAdjustment = glyphAdjustments.xAdvance; k_HandleGPOSFeaturesMarker.End(); } + + m_textInfo.characterInfo[m_characterCount].adjustedHorizontalAdvance = glyphAdjustments.xAdvance; + #endregion + + + // Handle Diacritical Marks + #region Handle Diacritical Marks + bool isBaseGlyph = TMP_TextParsingUtilities.IsBaseGlyph((uint)charCode); + + if (isBaseGlyph) + m_LastBaseGlyphIndex = m_characterCount; + + if (!isBaseGlyph && /* m_MarkToBaseEnabled && */ m_characterCount > 0) + { + Glyph baseGlyph = m_textInfo.characterInfo[m_LastBaseGlyphIndex].textElement.glyph; + uint baseGlyphIndex = baseGlyph.index; + uint markGlyphIndex = m_cached_TextElement.glyphIndex; + uint key = markGlyphIndex << 16 | baseGlyphIndex; + + // TODO: Add Type to glyph make it easier to identify the glyph type. + MarkToBaseAdjustmentRecord glyphAdjustmentRecord; + + if (m_currentFontAsset.fontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.TryGetValue(key, out glyphAdjustmentRecord)) + { + float advanceOffset = (m_textInfo.characterInfo[m_LastBaseGlyphIndex].origin - m_xAdvance) / currentElementScale; + + glyphAdjustments.xPlacement = advanceOffset + glyphAdjustmentRecord.baseGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.markPositionAdjustment.xPositionAdjustment; + glyphAdjustments.yPlacement = glyphAdjustmentRecord.baseGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.markPositionAdjustment.yPositionAdjustment; + + characterSpacingAdjustment = 0; + } + } + + if (!isBaseGlyph && /* m_MarkToMarkEnabled && */ m_characterCount > 0) + { + MarkToMarkAdjustmentRecord glyphAdjustmentRecord; + + Glyph baseGlyph = m_textInfo.characterInfo[m_characterCount - 1].textElement.glyph; + uint baseGlyphIndex = baseGlyph.index; + uint markGlyphIndex = m_cached_TextElement.glyphIndex; + uint key = markGlyphIndex << 16 | baseGlyphIndex; + + if (m_currentFontAsset.fontFeatureTable.m_MarkToMarkAdjustmentRecordLookup.TryGetValue(key, out glyphAdjustmentRecord)) + { + float baseMarkOrigin = (m_textInfo.characterInfo[m_characterCount - 1].origin - m_xAdvance) / currentElementScale; + float currentBaseline = baselineOffset - m_lineOffset + m_baselineOffset; + float baseMarkBaseline = (m_textInfo.characterInfo[m_characterCount - 1].baseLine - currentBaseline) / currentElementScale; + + glyphAdjustments.xPlacement = baseMarkOrigin + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.xPositionAdjustment; + glyphAdjustments.yPlacement = baseMarkBaseline + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.yPositionAdjustment; + + characterSpacingAdjustment = 0; + } + } + + // Adjust relevant text metrics + elementAscentLine += glyphAdjustments.yPlacement; + elementDescentLine += glyphAdjustments.yPlacement; #endregion @@ -2203,8 +2250,8 @@ protected virtual void GenerateTextMesh() m_textInfo.characterInfo[m_characterCount].topRight = top_right; m_textInfo.characterInfo[m_characterCount].bottomRight = bottom_right; - m_textInfo.characterInfo[m_characterCount].origin = m_xAdvance; - m_textInfo.characterInfo[m_characterCount].baseLine = baselineOffset - m_lineOffset + m_baselineOffset; + m_textInfo.characterInfo[m_characterCount].origin = m_xAdvance + glyphAdjustments.xPlacement * currentElementScale; + m_textInfo.characterInfo[m_characterCount].baseLine = (baselineOffset - m_lineOffset + m_baselineOffset) + glyphAdjustments.yPlacement * currentElementScale; m_textInfo.characterInfo[m_characterCount].aspectRatio = (top_right.x - bottom_left.x) / (top_left.y - bottom_left.y); @@ -2224,8 +2271,8 @@ protected virtual void GenerateTextMesh() float adjustedAscender = elementAscender; float adjustedDescender = elementDescender; - bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine; // Max line ascender and descender in line space + bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine; if (isFirstCharacterOfLine || isWhiteSpace == false) { // Special handling for Superscript and Subscript where we use the unadjusted line ascender and descender @@ -2284,7 +2331,7 @@ protected virtual void GenerateTextMesh() // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN. #region Handle Visible Characters - if (charCode == 9 || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) + if (charCode == 9 || ((m_TextWrappingMode == TextWrappingModes.PreserveWhitespace || m_TextWrappingMode == TextWrappingModes.PreserveWhitespaceNoWrap) && isWhiteSpace) || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) { k_HandleVisibleCharacterMarker.Begin(); @@ -2512,12 +2559,12 @@ protected virtual void GenerateTextMesh() // Handling of Horizontal Bounds #region Current Line Horizontal Bounds Check - if (textWidth > widthOfTextArea * (isJustifiedOrFlush ? 1.05f : 1.0f)) + if (isBaseGlyph && textWidth > widthOfTextArea * (isJustifiedOrFlush ? 1.05f : 1.0f)) { k_HandleHorizontalLineBreakingMarker.Begin(); // Handle Line Breaking (if still possible) - if (m_enableWordWrapping && m_characterCount != m_firstCharacterOfLine) + if (m_TextWrappingMode != TextWrappingModes.NoWrap && m_TextWrappingMode != TextWrappingModes.PreserveWhitespaceNoWrap && m_characterCount != m_firstCharacterOfLine) { // Restore state to previous safe line breaking i = RestoreWordWrappingState(ref m_SavedWordWrapState); @@ -2720,7 +2767,7 @@ protected virtual void GenerateTextMesh() case TextOverflowModes.Overflow: case TextOverflowModes.ScrollRect: case TextOverflowModes.Masking: - InsertNewLine(i, baseScale, currentElementScale, currentEmScale, m_GlyphHorizontalAdvanceAdjustment, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); + InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); isStartOfNewLine = true; isFirstWordOfLine = true; k_HandleVerticalLineBreakingMarker.End(); @@ -2789,7 +2836,7 @@ protected virtual void GenerateTextMesh() // Add new page m_isNewPage = true; - InsertNewLine(i, baseScale, currentElementScale, currentEmScale, m_GlyphHorizontalAdvanceAdjustment, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); + InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); m_startOfLineAscender = 0; m_lineOffset = 0; @@ -2833,7 +2880,7 @@ protected virtual void GenerateTextMesh() //} // New line of text does not exceed vertical bounds of text container - InsertNewLine(i, baseScale, currentElementScale, currentEmScale, m_GlyphHorizontalAdvanceAdjustment, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); + InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); isStartOfNewLine = true; isFirstWordOfLine = true; k_HandleHorizontalLineBreakingMarker.End(); @@ -2966,11 +3013,13 @@ protected virtual void GenerateTextMesh() // Special handling of characters that are not ignored at the end of a line. - if (charCode == 9) + if (isWhiteSpace) { m_textInfo.characterInfo[m_characterCount].isVisible = false; m_lastVisibleCharacterOfLine = m_characterCount; - m_textInfo.lineInfo[m_lineNumber].spaceCount += 1; + m_lineVisibleSpaceCount = m_textInfo.lineInfo[m_lineNumber].spaceCount += 1; + m_textInfo.lineInfo[m_lineNumber].marginLeft = marginLeft; + m_textInfo.lineInfo[m_lineNumber].marginRight = marginRight; m_textInfo.spaceCount += 1; } else if (charCode == 0xAD) @@ -3058,6 +3107,7 @@ protected virtual void GenerateTextMesh() m_textInfo.spaceCount += 1; } + // Special handling for control characters like if (charCode == 0xA0) m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1; @@ -3204,6 +3254,7 @@ protected virtual void GenerateTextMesh() m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1; m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount; + m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = m_lineVisibleSpaceCount; m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender); m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender); m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x - (padding * currentElementScale); @@ -3212,7 +3263,7 @@ protected virtual void GenerateTextMesh() if (m_textInfo.lineInfo[m_lineNumber].characterCount == 1) m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification; - float maxAdvanceOffset = ((m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale - m_cSpacing) * (1 - m_charWidthAdjDelta); + float maxAdvanceOffset = ((m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta); if (m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].isVisible) m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance + (m_isRightToLeft ? maxAdvanceOffset : - maxAdvanceOffset); else @@ -3236,6 +3287,7 @@ protected virtual void GenerateTextMesh() m_firstCharacterOfLine = m_characterCount + 1; m_lineVisibleCharacterCount = 0; + m_lineVisibleSpaceCount = 0; // Check to make sure Array is large enough to hold a new line. if (m_lineNumber >= m_textInfo.lineInfo.Length) @@ -3326,7 +3378,7 @@ protected virtual void GenerateTextMesh() // Save State of Mesh Creation for handling of Word Wrapping #region Save Word Wrapping State - if (m_enableWordWrapping || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis || m_overflowMode == TextOverflowModes.Linked) + if ((m_TextWrappingMode != TextWrappingModes.NoWrap && m_TextWrappingMode != TextWrappingModes.PreserveWhitespaceNoWrap) || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis || m_overflowMode == TextOverflowModes.Linked) { k_SaveProcessingStatesMarker.Begin(); @@ -3390,7 +3442,7 @@ protected virtual void GenerateTextMesh() else if (isFirstWordOfLine) { // Special handling for non-breaking space and soft line breaks - if (isWhiteSpace || (charCode == 0xAD && isSoftHyphenIgnored == false)) + if (isWhiteSpace && charCode != 0xA0 || (charCode == 0xAD && isSoftHyphenIgnored == false)) SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount); SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); @@ -3401,6 +3453,7 @@ protected virtual void GenerateTextMesh() } #endregion Save Word Wrapping State + // Consider only saving state on base glyphs SaveWordWrappingState(ref m_SavedLastValidState, i, m_characterCount); m_characterCount += 1; @@ -3504,7 +3557,6 @@ protected virtual void GenerateTextMesh() } #endregion - // Initialization for Second Pass Vector3 justificationOffset = Vector3.zero; Vector3 offset = Vector3.zero; @@ -3545,6 +3597,7 @@ protected virtual void GenerateTextMesh() TMP_FontAsset currentFontAsset = characterInfos[i].fontAsset; char unicode = characterInfos[i].character; + bool isWhiteSpace = char.IsWhiteSpace(unicode); int currentLine = characterInfos[i].lineNumber; TMP_LineInfo lineInfo = m_textInfo.lineInfo[currentLine]; @@ -3580,8 +3633,8 @@ protected virtual void GenerateTextMesh() case HorizontalAlignmentOptions.Justified: case HorizontalAlignmentOptions.Flush: - // Skip Zero Width Characters - if (unicode == 0x0A || unicode == 0xAD || unicode == 0x200B || unicode == 0x2060 || unicode == 0x03) break; + // Skip Zero Width Characters and spaces outside of the margins. + if (i > lineInfo.lastVisibleCharacterIndex || unicode == 0x0A || unicode == 0xAD || unicode == 0x200B || unicode == 0x2060 || unicode == 0x03) break; char lastCharOfCurrentLine = characterInfos[lineInfo.lastCharacterIndex].character; @@ -3607,11 +3660,8 @@ protected virtual void GenerateTextMesh() else { float gap = !m_isRightToLeft ? lineInfo.width - lineInfo.maxAdvance : lineInfo.width + lineInfo.maxAdvance; - int visibleCount = lineInfo.visibleCharacterCount - 1 + lineInfo.controlCharacterCount; - - // Get the number of spaces for each line ignoring the last character if it is not visible (ie. a space or linefeed). - int spaces = (characterInfos[lineInfo.lastCharacterIndex].isVisible ? lineInfo.spaceCount : lineInfo.spaceCount - 1) - lineInfo.controlCharacterCount; + int spaces = lineInfo.visibleSpaceCount - lineInfo.controlCharacterCount; if (isFirstSeperator) { spaces -= 1; visibleCount += 1; } @@ -3619,7 +3669,7 @@ protected virtual void GenerateTextMesh() if (spaces < 1) spaces = 1; - if (unicode != 0xA0 && (unicode == 9 || char.IsSeparator((char)unicode))) + if (unicode != 0xA0 && (unicode == 9 || char.IsSeparator(unicode))) { if (!m_isRightToLeft) justificationOffset += new Vector3(gap * (1 - ratio) / spaces, 0, 0); @@ -3777,31 +3827,11 @@ protected virtual void GenerateTextMesh() xScale = characterInfos[i].scale * Mathf.Abs(lossyScale) * (1 - m_charWidthAdjDelta); if (!characterInfos[i].isUsingAlternateTypeface && (characterInfos[i].style & FontStyles.Bold) == FontStyles.Bold) xScale *= -1; - //int isBold = (m_textInfo.characterInfo[i].style & FontStyles.Bold) == FontStyles.Bold ? 1 : 0; - //Vector2 vertexData = new Vector2(isBold, xScale); - //characterInfos[i].vertex_BL.uv4 = vertexData; - //characterInfos[i].vertex_TL.uv4 = vertexData; - //characterInfos[i].vertex_TR.uv4 = vertexData; - //characterInfos[i].vertex_BR.uv4 = vertexData; - - float x0 = characterInfos[i].vertex_BL.uv2.x; - float y0 = characterInfos[i].vertex_BL.uv2.y; - float x1 = characterInfos[i].vertex_TR.uv2.x; - float y1 = characterInfos[i].vertex_TR.uv2.y; - - float dx = (int)x0; - float dy = (int)y0; - - x0 = x0 - dx; - x1 = x1 - dx; - y0 = y0 - dy; - y1 = y1 - dy; - - // Optimization to avoid having a vector2 returned from the Pack UV function. - characterInfos[i].vertex_BL.uv2.x = PackUV(x0, y0); characterInfos[i].vertex_BL.uv2.y = xScale; - characterInfos[i].vertex_TL.uv2.x = PackUV(x0, y1); characterInfos[i].vertex_TL.uv2.y = xScale; - characterInfos[i].vertex_TR.uv2.x = PackUV(x1, y1); characterInfos[i].vertex_TR.uv2.y = xScale; - characterInfos[i].vertex_BR.uv2.x = PackUV(x1, y0); characterInfos[i].vertex_BR.uv2.y = xScale; + // Set SDF Scale + characterInfos[i].vertex_BL.uv.w = xScale; + characterInfos[i].vertex_TL.uv.w = xScale; + characterInfos[i].vertex_TR.uv.w = xScale; + characterInfos[i].vertex_BR.uv.w = xScale; #endregion break; @@ -3837,6 +3867,10 @@ protected virtual void GenerateTextMesh() } #endregion + if (QualitySettings.activeColorSpace == ColorSpace.Linear) + m_ConvertToLinearSpace = true; + else + m_ConvertToLinearSpace = false; // Fill Vertex Buffers for the various types of element if (elementType == TMP_TextElementType.Character) @@ -3934,7 +3968,7 @@ protected virtual void GenerateTextMesh() m_textInfo.lineInfo[currentLine].wordCount += 1; } } - else if (isStartOfWord || i == 0 && (!char.IsPunctuation(unicode) || char.IsWhiteSpace(unicode) || unicode == 0x200B || i == m_characterCount - 1)) + else if (isStartOfWord || i == 0 && (!char.IsPunctuation(unicode) || isWhiteSpace || unicode == 0x200B || i == m_characterCount - 1)) { if (i > 0 && i < characterInfos.Length - 1 && i < m_characterCount && (unicode == 39 || unicode == 8217) && char.IsLetterOrDigit(characterInfos[i - 1].character) && char.IsLetterOrDigit(characterInfos[i + 1].character)) { @@ -3978,7 +4012,7 @@ protected virtual void GenerateTextMesh() isUnderlineVisible = false; // We only use the scale of visible characters. - if (!char.IsWhiteSpace(unicode) && unicode != 0x200B) + if (!isWhiteSpace && unicode != 0x200B) { underlineMaxScale = Mathf.Max(underlineMaxScale, m_textInfo.characterInfo[i].scale); xScaleMax = Mathf.Max(xScaleMax, Mathf.Abs(xScale)); @@ -4019,7 +4053,7 @@ protected virtual void GenerateTextMesh() else if (beginUnderline && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex)) { // Terminate underline at previous visible character if space or carriage return. - if (char.IsWhiteSpace(unicode) || unicode == 0x200B) + if (isWhiteSpace || unicode == 0x200B) { int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex; underline_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, underlineBaseLine, 0); @@ -4120,7 +4154,7 @@ protected virtual void GenerateTextMesh() else if (beginStrikethrough && i == lineInfo.lastCharacterIndex) { // Terminate Strikethrough at previous visible character if space or carriage return. - if (char.IsWhiteSpace(unicode) || unicode == 0x200B) + if (isWhiteSpace || unicode == 0x200B) { int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex; strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0); @@ -4211,10 +4245,13 @@ protected virtual void GenerateTextMesh() bool isColorTransition = false; // Handle Highlight color changes - if (highlightState != currentCharacter.highlightState) + if (highlightState != currentState) { // Adjust previous highlight section to prevent a gaps between sections. - highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.bottomLeft.x) / 2; + if (isWhiteSpace) + highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.origin) / 2; + else + highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.bottomLeft.x) / 2; highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender); highlight_end.y = Mathf.Max(highlight_end.y, currentCharacter.ascender); @@ -4223,20 +4260,33 @@ protected virtual void GenerateTextMesh() beginHighlight = true; highlight_start = new Vector2(highlight_end.x, currentCharacter.descender - currentState.padding.bottom); - highlight_end = new Vector2(currentCharacter.topRight.x + currentState.padding.right, currentCharacter.ascender + currentState.padding.top); - highlightState = currentCharacter.highlightState; + if (isWhiteSpace) + highlight_end = new Vector2(currentCharacter.xAdvance + currentState.padding.right, currentCharacter.ascender + currentState.padding.top); + else + highlight_end = new Vector2(currentCharacter.topRight.x + currentState.padding.right, currentCharacter.ascender + currentState.padding.top); + + highlightState = currentState; isColorTransition = true; } if (!isColorTransition) { - // Use the Min / Max Extents of the Highlight area to handle different character sizes and fonts. - highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.bottomLeft.x - highlightState.padding.left); - highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender - highlightState.padding.bottom); + if (isWhiteSpace) + { + // Use the Min / Max of glyph metrics if white space. + highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.origin - highlightState.padding.left); + highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.xAdvance + highlightState.padding.right); + } + else + { + // Use the Min / Max of character bounds + highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.bottomLeft.x - highlightState.padding.left); + highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.topRight.x + highlightState.padding.right); + } - highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.topRight.x + highlightState.padding.right); + highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender - highlightState.padding.bottom); highlight_end.y = Mathf.Max(highlight_end.y, currentCharacter.ascender + highlightState.padding.top); } } @@ -4302,7 +4352,7 @@ protected virtual void GenerateTextMesh() // Upload Mesh Data m_mesh.MarkDynamic(); m_mesh.vertices = m_textInfo.meshInfo[0].vertices; - m_mesh.uv = m_textInfo.meshInfo[0].uvs0; + m_mesh.SetUVs(0, m_textInfo.meshInfo[0].uvs0); m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2; //m_mesh.uv4 = m_textInfo.meshInfo[0].uvs4; m_mesh.colors32 = m_textInfo.meshInfo[0].colors32; @@ -4323,7 +4373,7 @@ protected virtual void GenerateTextMesh() m_textInfo.meshInfo[i].SortGeometry(VertexSortingOrder.Reverse); m_subTextObjects[i].mesh.vertices = m_textInfo.meshInfo[i].vertices; - m_subTextObjects[i].mesh.uv = m_textInfo.meshInfo[i].uvs0; + m_subTextObjects[i].mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2; //m_subTextObjects[i].mesh.uv4 = m_textInfo.meshInfo[i].uvs4; m_subTextObjects[i].mesh.colors32 = m_textInfo.meshInfo[i].colors32; @@ -4541,19 +4591,19 @@ void UpdateSDFScale(float scaleDelta) { TMP_MeshInfo meshInfo = m_textInfo.meshInfo[materialIndex]; - for (int i = 0; i < meshInfo.uvs2.Length; i++) + for (int i = 0; i < meshInfo.uvs0.Length; i++) { - meshInfo.uvs2[i].y *= Mathf.Abs(scaleDelta); + meshInfo.uvs0[i].w *= Mathf.Abs(scaleDelta); } } - // Push the updated uv2 scale information to the meshes. + // Push the updated uv0 scale information to the meshes. for (int i = 0; i < m_textInfo.meshInfo.Length; i++) { if (i == 0) - m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2; + m_mesh.SetUVs(0, m_textInfo.meshInfo[0].uvs0); else - m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2; + m_subTextObjects[i].mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); } } diff --git a/Scripts/Runtime/TMPro_UGUI_Private.cs b/Scripts/Runtime/TMPro_UGUI_Private.cs index a9f6f03..3c43bfb 100644 --- a/Scripts/Runtime/TMPro_UGUI_Private.cs +++ b/Scripts/Runtime/TMPro_UGUI_Private.cs @@ -127,19 +127,6 @@ protected override void Awake() m_cached_TextElement = new TMP_Character(); m_isFirstAllocation = true; - // Check to make sure Sub Text Objects are tracked correctly in the event a Prefab is used. - TMP_SubMeshUI[] subTextObjects = GetComponentsInChildren(); - if (subTextObjects.Length > 0) - { - int subTextObjectCount = subTextObjects.Length; - - if (subTextObjectCount + 1 > m_subTextObjects.Length) - Array.Resize(ref m_subTextObjects, subTextObjectCount + 1); - - for (int i = 0; i < subTextObjectCount; i++) - m_subTextObjects[i + 1] = subTextObjects[i]; - } - // Set flags to ensure our text is parsed and redrawn. m_havePropertiesChanged = true; @@ -433,7 +420,7 @@ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) // Event received when font asset properties are changed in Font Inspector void ON_FONT_PROPERTY_CHANGED(bool isChanged, Object font) { - if (MaterialReference.Contains(m_materialReferences, (TMP_FontAsset) font)) + //if (MaterialReference.Contains(m_materialReferences, (TMP_FontAsset) font)) { //Debug.Log("ON_FONT_PROPERTY_CHANGED event received."); m_havePropertiesChanged = true; @@ -1228,6 +1215,8 @@ internal override int SetArraySizes(UnicodeChar[] unicodeChars) // Replace missing glyph by the Square (9633) glyph or possibly the Space (32) glyph. if (character == null) { + DoMissingGlyphCallback(unicode, unicodeChars[i].stringIndex, m_currentFontAsset); + // Save the original unicode character int srcGlyph = unicode; @@ -1530,6 +1519,8 @@ protected override void OnCanvasHierarchyChanged() TMP_UpdateManager.UnRegisterTextObjectForUpdate(this); else if (m_IsTextObjectScaleStatic == false) TMP_UpdateManager.RegisterTextObjectForUpdate(this); + + m_havePropertiesChanged = true; } @@ -1590,22 +1581,22 @@ internal override void InternalUpdate() { float lossyScaleY = m_rectTransform.lossyScale.y; - // Ignore very small lossy scale changes as their effect on SDF Scale would not be visually noticeable. - // Do not update SDF Scale if the text is null or empty - if (Mathf.Abs(lossyScaleY - m_previousLossyScaleY) > 0.0001f && m_TextProcessingArray[0].unicode != 0) + if (m_previousLossyScaleY != 0 && m_TextProcessingArray[0].unicode != 0) { float scaleDelta = lossyScaleY / m_previousLossyScaleY; - UpdateSDFScale(scaleDelta); - - m_previousLossyScaleY = lossyScaleY; + // Only update SDF Scale when lossy scale has changed by more than 20% + if (scaleDelta < 0.8f || scaleDelta > 1.25f) + { + UpdateSDFScale(scaleDelta); + m_previousLossyScaleY = lossyScaleY; + } } } // Added to handle legacy animation mode. if (m_isUsingLegacyAnimationComponent) { - //if (m_havePropertiesChanged) m_havePropertiesChanged = true; OnPreRenderCanvas(); } @@ -1625,17 +1616,18 @@ void OnPreRenderCanvas() if (m_canvas == null) { m_canvas = this.canvas; if (m_canvas == null) return; } - // Check if we have a font asset assigned. Return if we don't because no one likes to see purple squares on screen. - if (m_fontAsset == null) - { - Debug.LogWarning("Please assign a Font Asset to this " + transform.name + " gameobject.", this); - return; - } if (m_havePropertiesChanged || m_isLayoutDirty) { //Debug.Log("Properties have changed!"); // Assigned Material is:" + m_sharedMaterial); // New Text is: " + m_text + "."); + // Check if we have a font asset assigned. Return if we don't because no one likes to see purple squares on screen. + if (m_fontAsset == null) + { + Debug.LogWarning("Please assign a Font Asset to this " + transform.name + " gameobject.", this); + return; + } + // Update mesh padding if necessary. if (checkPaddingRequired) UpdateMeshPadding(); @@ -1820,6 +1812,7 @@ protected virtual void GenerateTextMesh() m_startOfLineAscender = 0; m_startOfLineDescender = 0; m_lineVisibleCharacterCount = 0; + m_lineVisibleSpaceCount = 0; bool isStartOfNewLine = true; m_IsDrivenLineSpacing = false; m_firstOverflowCharacterIndex = -1; @@ -2125,7 +2118,7 @@ protected virtual void GenerateTextMesh() m_textInfo.characterInfo[m_characterCount].color = m_htmlColor; m_textInfo.characterInfo[m_characterCount].underlineColor = m_underlineColor; m_textInfo.characterInfo[m_characterCount].strikethroughColor = m_strikethroughColor; - m_textInfo.characterInfo[m_characterCount].highlightState = m_HighlightStateStack.current; + m_textInfo.characterInfo[m_characterCount].highlightState = m_HighlightState; m_textInfo.characterInfo[m_characterCount].style = m_FontStyleInternal; // Cache glyph metrics @@ -2138,7 +2131,6 @@ protected virtual void GenerateTextMesh() #region Handle Kerning TMP_GlyphValueRecord glyphAdjustments = new TMP_GlyphValueRecord(); float characterSpacingAdjustment = m_characterSpacing; - m_GlyphHorizontalAdvanceAdjustment = 0; if (m_enableKerning) { k_HandleGPOSFeaturesMarker.Begin(); @@ -2151,7 +2143,7 @@ protected virtual void GenerateTextMesh() uint nextGlyphIndex = m_textInfo.characterInfo[m_characterCount + 1].textElement.m_GlyphIndex; uint key = nextGlyphIndex << 16 | baseGlyphIndex; - if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out adjustmentPair)) + if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair)) { glyphAdjustments = adjustmentPair.m_FirstAdjustmentRecord.m_GlyphValueRecord; characterSpacingAdjustment = (adjustmentPair.m_FeatureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; @@ -2163,16 +2155,73 @@ protected virtual void GenerateTextMesh() uint previousGlyphIndex = m_textInfo.characterInfo[m_characterCount - 1].textElement.m_GlyphIndex; uint key = baseGlyphIndex << 16 | previousGlyphIndex; - if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out adjustmentPair)) + if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair)) { glyphAdjustments += adjustmentPair.m_SecondAdjustmentRecord.m_GlyphValueRecord; characterSpacingAdjustment = (adjustmentPair.m_FeatureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; } } - m_GlyphHorizontalAdvanceAdjustment = glyphAdjustments.xAdvance; k_HandleGPOSFeaturesMarker.End(); } + + m_textInfo.characterInfo[m_characterCount].adjustedHorizontalAdvance = glyphAdjustments.xAdvance; + #endregion + + + // Handle Diacritical Marks + #region Handle Diacritical Marks + bool isBaseGlyph = TMP_TextParsingUtilities.IsBaseGlyph((uint)charCode); + + if (isBaseGlyph) + m_LastBaseGlyphIndex = m_characterCount; + + if (!isBaseGlyph && /* m_MarkToBaseEnabled && */ m_characterCount > 0) + { + Glyph baseGlyph = m_textInfo.characterInfo[m_LastBaseGlyphIndex].textElement.glyph; + uint baseGlyphIndex = baseGlyph.index; + uint markGlyphIndex = m_cached_TextElement.glyphIndex; + uint key = markGlyphIndex << 16 | baseGlyphIndex; + + // TODO: Add Type to glyph make it easier to identify the glyph type. + MarkToBaseAdjustmentRecord glyphAdjustmentRecord; + + if (m_currentFontAsset.fontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.TryGetValue(key, out glyphAdjustmentRecord)) + { + float advanceOffset = (m_textInfo.characterInfo[m_LastBaseGlyphIndex].origin - m_xAdvance) / currentElementScale; + + glyphAdjustments.xPlacement = advanceOffset + glyphAdjustmentRecord.baseGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.markPositionAdjustment.xPositionAdjustment; + glyphAdjustments.yPlacement = glyphAdjustmentRecord.baseGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.markPositionAdjustment.yPositionAdjustment; + + characterSpacingAdjustment = 0; + } + } + + if (!isBaseGlyph && /* m_MarkToMarkEnabled && */ m_characterCount > 0) + { + MarkToMarkAdjustmentRecord glyphAdjustmentRecord; + + Glyph baseGlyph = m_textInfo.characterInfo[m_characterCount - 1].textElement.glyph; + uint baseGlyphIndex = baseGlyph.index; + uint markGlyphIndex = m_cached_TextElement.glyphIndex; + uint key = markGlyphIndex << 16 | baseGlyphIndex; + + if (m_currentFontAsset.fontFeatureTable.m_MarkToMarkAdjustmentRecordLookup.TryGetValue(key, out glyphAdjustmentRecord)) + { + float baseMarkOrigin = (m_textInfo.characterInfo[m_characterCount - 1].origin - m_xAdvance) / currentElementScale; + float currentBaseline = baselineOffset - m_lineOffset + m_baselineOffset; + float baseMarkBaseline = (m_textInfo.characterInfo[m_characterCount - 1].baseLine - currentBaseline) / currentElementScale; + + glyphAdjustments.xPlacement = baseMarkOrigin + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.xPositionAdjustment; + glyphAdjustments.yPlacement = baseMarkBaseline + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.yPositionAdjustment; + + characterSpacingAdjustment = 0; + } + } + + // Adjust relevant text metrics + elementAscentLine += glyphAdjustments.yPlacement; + elementDescentLine += glyphAdjustments.yPlacement; #endregion @@ -2311,8 +2360,8 @@ protected virtual void GenerateTextMesh() m_textInfo.characterInfo[m_characterCount].topRight = top_right; m_textInfo.characterInfo[m_characterCount].bottomRight = bottom_right; - m_textInfo.characterInfo[m_characterCount].origin = m_xAdvance; - m_textInfo.characterInfo[m_characterCount].baseLine = baselineOffset - m_lineOffset + m_baselineOffset; + m_textInfo.characterInfo[m_characterCount].origin = m_xAdvance + glyphAdjustments.xPlacement * currentElementScale; + m_textInfo.characterInfo[m_characterCount].baseLine = (baselineOffset - m_lineOffset + m_baselineOffset) + glyphAdjustments.yPlacement * currentElementScale; m_textInfo.characterInfo[m_characterCount].aspectRatio = (top_right.x - bottom_left.x) / (top_left.y - bottom_left.y); @@ -2332,8 +2381,8 @@ protected virtual void GenerateTextMesh() float adjustedAscender = elementAscender; float adjustedDescender = elementDescender; - bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine; // Max line ascender and descender in line space + bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine; if (isFirstCharacterOfLine || isWhiteSpace == false) { // Special handling for Superscript and Subscript where we use the unadjusted line ascender and descender @@ -2392,7 +2441,7 @@ protected virtual void GenerateTextMesh() // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN. #region Handle Visible Characters - if (charCode == 9 || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) + if (charCode == 9 || ((m_TextWrappingMode == TextWrappingModes.PreserveWhitespace || m_TextWrappingMode == TextWrappingModes.PreserveWhitespaceNoWrap) && isWhiteSpace) || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite) { k_HandleVisibleCharacterMarker.Begin(); @@ -2620,12 +2669,12 @@ protected virtual void GenerateTextMesh() // Handling of Horizontal Bounds #region Current Line Horizontal Bounds Check - if (textWidth > widthOfTextArea * (isJustifiedOrFlush ? 1.05f : 1.0f)) + if (isBaseGlyph && textWidth > widthOfTextArea * (isJustifiedOrFlush ? 1.05f : 1.0f)) { k_HandleHorizontalLineBreakingMarker.Begin(); // Handle Line Breaking (if still possible) - if (m_enableWordWrapping && m_characterCount != m_firstCharacterOfLine) + if (m_TextWrappingMode != TextWrappingModes.NoWrap && m_TextWrappingMode != TextWrappingModes.PreserveWhitespaceNoWrap && m_characterCount != m_firstCharacterOfLine) { // Restore state to previous safe line breaking i = RestoreWordWrappingState(ref m_SavedWordWrapState); @@ -2828,7 +2877,7 @@ protected virtual void GenerateTextMesh() case TextOverflowModes.Overflow: case TextOverflowModes.ScrollRect: case TextOverflowModes.Masking: - InsertNewLine(i, baseScale, currentElementScale, currentEmScale, m_GlyphHorizontalAdvanceAdjustment, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); + InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); isStartOfNewLine = true; isFirstWordOfLine = true; k_HandleVerticalLineBreakingMarker.End(); @@ -2897,7 +2946,7 @@ protected virtual void GenerateTextMesh() // Add new page m_isNewPage = true; - InsertNewLine(i, baseScale, currentElementScale, currentEmScale, m_GlyphHorizontalAdvanceAdjustment, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); + InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); m_startOfLineAscender = 0; m_lineOffset = 0; @@ -2941,7 +2990,7 @@ protected virtual void GenerateTextMesh() //} // New line of text does not exceed vertical bounds of text container - InsertNewLine(i, baseScale, currentElementScale, currentEmScale, m_GlyphHorizontalAdvanceAdjustment, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); + InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender); isStartOfNewLine = true; isFirstWordOfLine = true; k_HandleHorizontalLineBreakingMarker.End(); @@ -3074,11 +3123,13 @@ protected virtual void GenerateTextMesh() // Special handling of characters that are not ignored at the end of a line. - if (charCode == 9) + if (isWhiteSpace) { m_textInfo.characterInfo[m_characterCount].isVisible = false; m_lastVisibleCharacterOfLine = m_characterCount; - m_textInfo.lineInfo[m_lineNumber].spaceCount += 1; + m_lineVisibleSpaceCount = m_textInfo.lineInfo[m_lineNumber].spaceCount += 1; + m_textInfo.lineInfo[m_lineNumber].marginLeft = marginLeft; + m_textInfo.lineInfo[m_lineNumber].marginRight = marginRight; m_textInfo.spaceCount += 1; } else if (charCode == 0xAD) @@ -3166,6 +3217,7 @@ protected virtual void GenerateTextMesh() m_textInfo.spaceCount += 1; } + // Special handling for control characters like if (charCode == 0xA0) m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1; @@ -3312,6 +3364,7 @@ protected virtual void GenerateTextMesh() m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1; m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount; + m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = m_lineVisibleSpaceCount; m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender); m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender); m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x - (padding * currentElementScale); @@ -3320,7 +3373,7 @@ protected virtual void GenerateTextMesh() if (m_textInfo.lineInfo[m_lineNumber].characterCount == 1) m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification; - float maxAdvanceOffset = ((m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale - m_cSpacing) * (1 - m_charWidthAdjDelta); + float maxAdvanceOffset = ((m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta); if (m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].isVisible) m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance + (m_isRightToLeft ? maxAdvanceOffset : - maxAdvanceOffset); else @@ -3344,6 +3397,7 @@ protected virtual void GenerateTextMesh() m_firstCharacterOfLine = m_characterCount + 1; m_lineVisibleCharacterCount = 0; + m_lineVisibleSpaceCount = 0; // Check to make sure Array is large enough to hold a new line. if (m_lineNumber >= m_textInfo.lineInfo.Length) @@ -3434,7 +3488,7 @@ protected virtual void GenerateTextMesh() // Save State of Mesh Creation for handling of Word Wrapping #region Save Word Wrapping State - if (m_enableWordWrapping || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis || m_overflowMode == TextOverflowModes.Linked) + if ((m_TextWrappingMode != TextWrappingModes.NoWrap && m_TextWrappingMode != TextWrappingModes.PreserveWhitespaceNoWrap) || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis || m_overflowMode == TextOverflowModes.Linked) { k_SaveProcessingStatesMarker.Begin(); @@ -3498,7 +3552,7 @@ protected virtual void GenerateTextMesh() else if (isFirstWordOfLine) { // Special handling for non-breaking space and soft line breaks - if (isWhiteSpace || (charCode == 0xAD && isSoftHyphenIgnored == false)) + if (isWhiteSpace && charCode != 0xA0 || (charCode == 0xAD && isSoftHyphenIgnored == false)) SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount); SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); @@ -3509,6 +3563,7 @@ protected virtual void GenerateTextMesh() } #endregion Save Word Wrapping State + // Consider only saving state on base glyphs SaveWordWrappingState(ref m_SavedLastValidState, i, m_characterCount); m_characterCount += 1; @@ -3612,7 +3667,6 @@ protected virtual void GenerateTextMesh() } #endregion - // Initialization for Second Pass Vector3 justificationOffset = Vector3.zero; Vector3 offset = Vector3.zero; @@ -3657,6 +3711,7 @@ protected virtual void GenerateTextMesh() TMP_FontAsset currentFontAsset = characterInfos[i].fontAsset; char unicode = characterInfos[i].character; + bool isWhiteSpace = char.IsWhiteSpace(unicode); int currentLine = characterInfos[i].lineNumber; TMP_LineInfo lineInfo = m_textInfo.lineInfo[currentLine]; @@ -3692,8 +3747,8 @@ protected virtual void GenerateTextMesh() case HorizontalAlignmentOptions.Justified: case HorizontalAlignmentOptions.Flush: - // Skip Zero Width Characters - if (unicode == 0x0A || unicode == 0xAD || unicode == 0x200B || unicode == 0x2060 || unicode == 0x03) break; + // Skip Zero Width Characters and spaces outside of the margins. + if (i > lineInfo.lastVisibleCharacterIndex || unicode == 0x0A || unicode == 0xAD || unicode == 0x200B || unicode == 0x2060 || unicode == 0x03) break; char lastCharOfCurrentLine = characterInfos[lineInfo.lastCharacterIndex].character; @@ -3719,11 +3774,8 @@ protected virtual void GenerateTextMesh() else { float gap = !m_isRightToLeft ? lineInfo.width - lineInfo.maxAdvance : lineInfo.width + lineInfo.maxAdvance; - int visibleCount = lineInfo.visibleCharacterCount - 1 + lineInfo.controlCharacterCount; - - // Get the number of spaces for each line ignoring the last character if it is not visible (ie. a space or linefeed). - int spaces = (characterInfos[lineInfo.lastCharacterIndex].isVisible ? lineInfo.spaceCount : lineInfo.spaceCount - 1) - lineInfo.controlCharacterCount; + int spaces = lineInfo.visibleSpaceCount - lineInfo.controlCharacterCount; if (isFirstSeperator) { spaces -= 1; visibleCount += 1; } @@ -3731,7 +3783,7 @@ protected virtual void GenerateTextMesh() if (spaces < 1) spaces = 1; - if (unicode != 0xA0 && (unicode == 9 || char.IsSeparator((char)unicode))) + if (unicode != 0xA0 && (unicode == 9 || char.IsSeparator(unicode))) { if (!m_isRightToLeft) justificationOffset += new Vector3(gap * (1 - ratio) / spaces, 0, 0); @@ -3902,31 +3954,11 @@ protected virtual void GenerateTextMesh() break; } - // isBold is encoded in the X value and SDF Scale in Y. - //Vector2 vertexData = new Vector2((characterInfos[i].style & FontStyles.Bold) == FontStyles.Bold ? 1 : 0, xScale); - //characterInfos[i].vertex_BL.uv2 = vertexData; - //characterInfos[i].vertex_TL.uv2 = vertexData; - //characterInfos[i].vertex_TR.uv2 = vertexData; - //characterInfos[i].vertex_BR.uv2 = vertexData; - - float x0 = characterInfos[i].vertex_BL.uv2.x; - float y0 = characterInfos[i].vertex_BL.uv2.y; - float x1 = characterInfos[i].vertex_TR.uv2.x; - float y1 = characterInfos[i].vertex_TR.uv2.y; - - float dx = (int)x0; - float dy = (int)y0; - - x0 = x0 - dx; - x1 = x1 - dx; - y0 = y0 - dy; - y1 = y1 - dy; - - // Optimization to avoid having a vector2 returned from the Pack UV function. - characterInfos[i].vertex_BL.uv2.x = PackUV(x0, y0); characterInfos[i].vertex_BL.uv2.y = xScale; - characterInfos[i].vertex_TL.uv2.x = PackUV(x0, y1); characterInfos[i].vertex_TL.uv2.y = xScale; - characterInfos[i].vertex_TR.uv2.x = PackUV(x1, y1); characterInfos[i].vertex_TR.uv2.y = xScale; - characterInfos[i].vertex_BR.uv2.x = PackUV(x1, y0); characterInfos[i].vertex_BR.uv2.y = xScale; + // Set SDF Scale + characterInfos[i].vertex_BL.uv.w = xScale; + characterInfos[i].vertex_TL.uv.w = xScale; + characterInfos[i].vertex_TR.uv.w = xScale; + characterInfos[i].vertex_BR.uv.w = xScale; #endregion break; @@ -4059,7 +4091,7 @@ protected virtual void GenerateTextMesh() m_textInfo.lineInfo[currentLine].wordCount += 1; } } - else if (isStartOfWord || i == 0 && (!char.IsPunctuation(unicode) || char.IsWhiteSpace(unicode) || unicode == 0x200B || i == m_characterCount - 1)) + else if (isStartOfWord || i == 0 && (!char.IsPunctuation(unicode) || isWhiteSpace || unicode == 0x200B || i == m_characterCount - 1)) { if (i > 0 && i < characterInfos.Length - 1 && i < m_characterCount && (unicode == 39 || unicode == 8217) && char.IsLetterOrDigit(characterInfos[i - 1].character) && char.IsLetterOrDigit(characterInfos[i + 1].character)) { @@ -4103,7 +4135,7 @@ protected virtual void GenerateTextMesh() isUnderlineVisible = false; // We only use the scale of visible characters. - if (!char.IsWhiteSpace(unicode) && unicode != 0x200B) + if (!isWhiteSpace && unicode != 0x200B) { underlineMaxScale = Mathf.Max(underlineMaxScale, m_textInfo.characterInfo[i].scale); xScaleMax = Mathf.Max(xScaleMax, Mathf.Abs(xScale)); @@ -4144,7 +4176,7 @@ protected virtual void GenerateTextMesh() else if (beginUnderline && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex)) { // Terminate underline at previous visible character if space or carriage return. - if (char.IsWhiteSpace(unicode) || unicode == 0x200B) + if (isWhiteSpace || unicode == 0x200B) { int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex; underline_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, underlineBaseLine, 0); @@ -4245,7 +4277,7 @@ protected virtual void GenerateTextMesh() else if (beginStrikethrough && i == lineInfo.lastCharacterIndex) { // Terminate Strikethrough at previous visible character if space or carriage return. - if (char.IsWhiteSpace(unicode) || unicode == 0x200B) + if (isWhiteSpace || unicode == 0x200B) { int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex; strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0); @@ -4336,10 +4368,13 @@ protected virtual void GenerateTextMesh() bool isColorTransition = false; // Handle Highlight color changes - if (highlightState != currentCharacter.highlightState) + if (highlightState != currentState) { // Adjust previous highlight section to prevent a gaps between sections. - highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.bottomLeft.x) / 2; + if (isWhiteSpace) + highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.origin) / 2; + else + highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.bottomLeft.x) / 2; highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender); highlight_end.y = Mathf.Max(highlight_end.y, currentCharacter.ascender); @@ -4348,20 +4383,33 @@ protected virtual void GenerateTextMesh() beginHighlight = true; highlight_start = new Vector2(highlight_end.x, currentCharacter.descender - currentState.padding.bottom); - highlight_end = new Vector2(currentCharacter.topRight.x + currentState.padding.right, currentCharacter.ascender + currentState.padding.top); - highlightState = currentCharacter.highlightState; + if (isWhiteSpace) + highlight_end = new Vector2(currentCharacter.xAdvance + currentState.padding.right, currentCharacter.ascender + currentState.padding.top); + else + highlight_end = new Vector2(currentCharacter.topRight.x + currentState.padding.right, currentCharacter.ascender + currentState.padding.top); + + highlightState = currentState; isColorTransition = true; } if (!isColorTransition) { - // Use the Min / Max Extents of the Highlight area to handle different character sizes and fonts. - highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.bottomLeft.x - highlightState.padding.left); - highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender - highlightState.padding.bottom); + if (isWhiteSpace) + { + // Use the Min / Max of glyph metrics if white space. + highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.origin - highlightState.padding.left); + highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.xAdvance + highlightState.padding.right); + } + else + { + // Use the Min / Max of character bounds + highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.bottomLeft.x - highlightState.padding.left); + highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.topRight.x + highlightState.padding.right); + } - highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.topRight.x + highlightState.padding.right); + highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender - highlightState.padding.bottom); highlight_end.y = Mathf.Max(highlight_end.y, currentCharacter.ascender + highlightState.padding.top); } } @@ -4432,7 +4480,7 @@ protected virtual void GenerateTextMesh() // Upload Mesh Data m_mesh.MarkDynamic(); m_mesh.vertices = m_textInfo.meshInfo[0].vertices; - m_mesh.uv = m_textInfo.meshInfo[0].uvs0; + m_mesh.SetUVs(0, m_textInfo.meshInfo[0].uvs0); m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2; //m_mesh.uv4 = m_textInfo.meshInfo[0].uvs4; m_mesh.colors32 = m_textInfo.meshInfo[0].colors32; @@ -4461,7 +4509,7 @@ protected virtual void GenerateTextMesh() //m_subTextObjects[i].mesh.MarkDynamic(); m_subTextObjects[i].mesh.vertices = m_textInfo.meshInfo[i].vertices; - m_subTextObjects[i].mesh.uv = m_textInfo.meshInfo[i].uvs0; + m_subTextObjects[i].mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2; //m_subTextObjects[i].mesh.uv4 = m_textInfo.meshInfo[i].uvs4; m_subTextObjects[i].mesh.colors32 = m_textInfo.meshInfo[i].colors32; @@ -4481,6 +4529,10 @@ protected virtual void GenerateTextMesh() } } + // Update culling if it has to be delayed due to text layout being dirty. + if (m_ShouldUpdateCulling) + UpdateCulling(); + // Event indicating the text has been regenerated. TMPro_EventManager.ON_TEXT_CHANGED(this); @@ -4651,23 +4703,23 @@ void UpdateSDFScale(float scaleDelta) { TMP_MeshInfo meshInfo = m_textInfo.meshInfo[materialIndex]; - for (int i = 0; i < meshInfo.uvs2.Length; i++) + for (int i = 0; i < meshInfo.uvs0.Length; i++) { - meshInfo.uvs2[i].y *= Mathf.Abs(scaleDelta); + meshInfo.uvs0[i].w *= Mathf.Abs(scaleDelta); } } - // Push the updated uv2 scale information to the meshes. + // Push the updated uv0 scale information to the meshes. for (int i = 0; i < m_textInfo.materialCount; i++) { if (i == 0) { - m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2; + m_mesh.SetUVs(0, m_textInfo.meshInfo[0].uvs0); m_canvasRenderer.SetMesh(m_mesh); } else { - m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2; + m_subTextObjects[i].mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); m_subTextObjects[i].canvasRenderer.SetMesh(m_subTextObjects[i].mesh); } } diff --git a/Scripts/Runtime/TextMeshPro.cs b/Scripts/Runtime/TextMeshPro.cs index 4ab5d8a..f173989 100644 --- a/Scripts/Runtime/TextMeshPro.cs +++ b/Scripts/Runtime/TextMeshPro.cs @@ -9,7 +9,7 @@ namespace TMPro [RequireComponent(typeof(MeshRenderer))] [AddComponentMenu("Mesh/TextMeshPro - Text")] [ExecuteAlways] - [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.0")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2")] public partial class TextMeshPro : TMP_Text, ILayoutElement { // Public Properties and Serializable Properties @@ -416,7 +416,7 @@ public override void UpdateVertexData(TMP_VertexDataUpdateFlags flags) mesh.vertices = m_textInfo.meshInfo[i].vertices; if ((flags & TMP_VertexDataUpdateFlags.Uv0) == TMP_VertexDataUpdateFlags.Uv0) - mesh.uv = m_textInfo.meshInfo[i].uvs0; + mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); if ((flags & TMP_VertexDataUpdateFlags.Uv2) == TMP_VertexDataUpdateFlags.Uv2) mesh.uv2 = m_textInfo.meshInfo[i].uvs2; @@ -456,7 +456,7 @@ public override void UpdateVertexData() //mesh.MarkDynamic(); mesh.vertices = m_textInfo.meshInfo[i].vertices; - mesh.uv = m_textInfo.meshInfo[i].uvs0; + mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); mesh.uv2 = m_textInfo.meshInfo[i].uvs2; //mesh.uv4 = m_textInfo.meshInfo[i].uvs4; mesh.colors32 = m_textInfo.meshInfo[i].colors32; diff --git a/Scripts/Runtime/TextMeshProUGUI.cs b/Scripts/Runtime/TextMeshProUGUI.cs index d284006..9c530ac 100644 --- a/Scripts/Runtime/TextMeshProUGUI.cs +++ b/Scripts/Runtime/TextMeshProUGUI.cs @@ -14,7 +14,7 @@ namespace TMPro [RequireComponent(typeof(CanvasRenderer))] [AddComponentMenu("UI/TextMeshPro - Text (UI)", 11)] [ExecuteAlways] - [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.0")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2")] public partial class TextMeshProUGUI : TMP_Text, ILayoutElement { /// @@ -364,10 +364,12 @@ public override void RecalculateClipping() /// public override void Cull(Rect clipRect, bool validRect) { + m_ShouldUpdateCulling = false; + // Delay culling check in the event the text layout is dirty and geometry has to be updated. if (m_isLayoutDirty) { - TMP_UpdateManager.RegisterTextElementForCullingUpdate(this); + m_ShouldUpdateCulling = true; m_ClipRect = clipRect; m_ValidRect = validRect; return; @@ -377,8 +379,8 @@ public override void Cull(Rect clipRect, bool validRect) Rect rect = GetCanvasSpaceClippingRect(); // No point culling if geometry bounds have no width or height. - if (rect.width == 0 || rect.height == 0) - return; + //if (rect.width == 0 || rect.height == 0) + // return; var cull = !validRect || !clipRect.Overlaps(rect, true); if (m_canvasRenderer.cull != cull) @@ -395,6 +397,7 @@ public override void Cull(Rect clipRect, bool validRect) } } + private bool m_ShouldUpdateCulling; private Rect m_ClipRect; private bool m_ValidRect; @@ -407,8 +410,8 @@ internal override void UpdateCulling() Rect rect = GetCanvasSpaceClippingRect(); // No point culling if geometry bounds have no width or height. - if (rect.width == 0 || rect.height == 0) - return; + //if (rect.width == 0 || rect.height == 0) + // return; var cull = !m_ValidRect || !m_ClipRect.Overlaps(rect, true); if (m_canvasRenderer.cull != cull) @@ -423,6 +426,8 @@ internal override void UpdateCulling() m_subTextObjects[i].canvasRenderer.cull = cull; } } + + m_ShouldUpdateCulling = false; } @@ -637,7 +642,7 @@ public override void UpdateVertexData(TMP_VertexDataUpdateFlags flags) mesh.vertices = m_textInfo.meshInfo[i].vertices; if ((flags & TMP_VertexDataUpdateFlags.Uv0) == TMP_VertexDataUpdateFlags.Uv0) - mesh.uv = m_textInfo.meshInfo[i].uvs0; + mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); if ((flags & TMP_VertexDataUpdateFlags.Uv2) == TMP_VertexDataUpdateFlags.Uv2) mesh.uv2 = m_textInfo.meshInfo[i].uvs2; @@ -681,7 +686,7 @@ public override void UpdateVertexData() //mesh.MarkDynamic(); mesh.vertices = m_textInfo.meshInfo[i].vertices; - mesh.uv = m_textInfo.meshInfo[i].uvs0; + mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0); mesh.uv2 = m_textInfo.meshInfo[i].uvs2; //mesh.uv4 = m_textInfo.meshInfo[i].uvs4; mesh.colors32 = m_textInfo.meshInfo[i].colors32; diff --git a/Scripts/Runtime/Unity.TextMeshPro.asmdef b/Scripts/Runtime/Unity.TextMeshPro.asmdef index 2abb4e4..7b3ba6c 100644 --- a/Scripts/Runtime/Unity.TextMeshPro.asmdef +++ b/Scripts/Runtime/Unity.TextMeshPro.asmdef @@ -1,12 +1,14 @@ { "name": "Unity.TextMeshPro", + "rootNamespace": "", "references": [], - "optionalUnityReferences": [], "includePlatforms": [], "excludePlatforms": [], - "allowUnsafeCode": false, + "allowUnsafeCode": true, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [] + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/Tests/Editor/TMP_EditorTests.cs b/Tests/Editor/TMP_EditorTests.cs index a31a640..d22b305 100644 --- a/Tests/Editor/TMP_EditorTests.cs +++ b/Tests/Editor/TMP_EditorTests.cs @@ -1,9 +1,7 @@ using UnityEngine; using UnityEditor; -using UnityEngine.TestTools; using NUnit.Framework; using System.IO; -using System.Collections; namespace TMPro @@ -19,7 +17,7 @@ class TMP_EditorTests // Characters: 104 Spaces: 14 Words: 15 Lines: private const string m_TextBlock_01 = "Unity 2017 introduces new features that help teams of artists and developers build experiences together."; - // Characters: 1500 Spaces: 228 Words: 241 + // Characters: 1500 Spaces: 228 Words: 241 private const string m_TextBlock_02 = "The European languages are members of the same family. Their separate existence is a myth. For science, music, sport, etc, Europe uses the same vocabulary. The languages only differ in their grammar, their pronunciation and their most common words." + "Everyone realizes why a new common language would be desirable: one could refuse to pay expensive translators.To achieve this, it would be necessary to have uniform grammar, pronunciation and more common words.If several languages coalesce, the grammar of the resulting language is more simple and regular than that of the individual languages." + "The new common language will be more simple and regular than the existing European languages.It will be as simple as Occidental; in fact, it will be Occidental.To an English person, it will seem like simplified English, as a skeptical Cambridge friend of mine told me what Occidental is. The European languages are members of the same family." + @@ -42,7 +40,7 @@ class TMP_EditorTests "Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi.Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo.Maecenas malesuada. Praesent congue erat at massa.Sed cursus turpis vitae tortor.Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci.Phasellus consectetuer vestibulum elit.Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc.Vestibulum fringilla pede sit amet augue." + "In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis.Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus.Ut varius tincidunt libero.Phasellus dolor.Maecenas vestibulum mollis"; - // + // private const string m_TextBlock_05 = "This block of text contains bold and italicized characters."; private const string m_TextBlock_06 = "<#ffffff>Multiple<#80f0ff> Alignment per text object\n" + @@ -101,7 +99,7 @@ public void InternalResourceCheck(string filePath, string guid) public void TextParsing_TextInfoTest_WordWrappingDisabled(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount) { m_TextComponent.text = testStrings[sourceTextIndex]; - m_TextComponent.enableWordWrapping = false; + m_TextComponent.textWrappingMode = TextWrappingModes.NoWrap; m_TextComponent.alignment = TextAlignmentOptions.TopLeft; // Size the RectTransform @@ -126,7 +124,7 @@ public void TextParsing_TextInfoTest_WordWrappingDisabled(int sourceTextIndex, i public void TextParsing_TextInfoTest_WordWrappingEnabled(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount) { m_TextComponent.text = testStrings[sourceTextIndex]; - m_TextComponent.enableWordWrapping = true; + m_TextComponent.textWrappingMode = TextWrappingModes.Normal; m_TextComponent.alignment = TextAlignmentOptions.TopLeft; // Size the RectTransform @@ -149,7 +147,7 @@ public void TextParsing_TextInfoTest_WordWrappingEnabled(int sourceTextIndex, in public void TextParsing_TextInfoTest_TopJustifiedAlignment(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount) { m_TextComponent.text = testStrings[sourceTextIndex]; - m_TextComponent.enableWordWrapping = true; + m_TextComponent.textWrappingMode = TextWrappingModes.Normal; m_TextComponent.alignment = TextAlignmentOptions.TopJustified; // Size the RectTransform @@ -171,7 +169,7 @@ public void TextParsing_TextInfoTest_TopJustifiedAlignment(int sourceTextIndex, public void TextParsing_TextInfoTest_RichText(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount) { m_TextComponent.text = testStrings[sourceTextIndex]; - m_TextComponent.enableWordWrapping = true; + m_TextComponent.textWrappingMode = TextWrappingModes.Normal; m_TextComponent.alignment = TextAlignmentOptions.TopLeft; // Size the RectTransform diff --git a/Tests/Runtime/TMP_RuntimeTests.cs b/Tests/Runtime/TMP_RuntimeTests.cs index 7b1834d..c4d2c1f 100644 --- a/Tests/Runtime/TMP_RuntimeTests.cs +++ b/Tests/Runtime/TMP_RuntimeTests.cs @@ -1,10 +1,9 @@ using UnityEngine; -using UnityEngine.TestTools; using NUnit.Framework; using System.IO; -using System.Collections; using System.Collections.Generic; + namespace TMPro { [Category("Text Parsing & Layout")] @@ -18,7 +17,7 @@ class TMP_RuntimeTests // Characters: 104 Spaces: 14 Words: 15 Lines: private const string m_TextBlock_01 = "Unity 2017 introduces new features that help teams of artists and developers build experiences together."; - // Characters: 1500 Spaces: 228 Words: 241 + // Characters: 1500 Spaces: 228 Words: 241 private const string m_TextBlock_02 = "The European languages are members of the same family. Their separate existence is a myth. For science, music, sport, etc, Europe uses the same vocabulary. The languages only differ in their grammar, their pronunciation and their most common words." + "Everyone realizes why a new common language would be desirable: one could refuse to pay expensive translators.To achieve this, it would be necessary to have uniform grammar, pronunciation and more common words.If several languages coalesce, the grammar of the resulting language is more simple and regular than that of the individual languages." + "The new common language will be more simple and regular than the existing European languages.It will be as simple as Occidental; in fact, it will be Occidental.To an English person, it will seem like simplified English, as a skeptical Cambridge friend of mine told me what Occidental is. The European languages are members of the same family." + @@ -41,7 +40,7 @@ class TMP_RuntimeTests "Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi.Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo.Maecenas malesuada. Praesent congue erat at massa.Sed cursus turpis vitae tortor.Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci.Phasellus consectetuer vestibulum elit.Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc.Vestibulum fringilla pede sit amet augue." + "In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis.Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus.Ut varius tincidunt libero.Phasellus dolor.Maecenas vestibulum mollis"; - // + // private const string m_TextBlock_05 = "This block of text contains bold and italicized characters."; private const string m_TextBlock_06 = "<#ffffff>Multiple<#80f0ff> Alignment per text object\n" + @@ -87,7 +86,7 @@ public static IEnumerable TestCases_Parsing_TextInfo_WordWrapDisabled( public void Parsing_TextInfo_WordWrapDisabled(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount) { m_TextComponent.text = testStrings[sourceTextIndex]; - m_TextComponent.enableWordWrapping = false; + m_TextComponent.textWrappingMode = TextWrappingModes.NoWrap; m_TextComponent.alignment = TextAlignmentOptions.TopLeft; // Size the RectTransform @@ -116,7 +115,7 @@ public static IEnumerable TestCases_Parsing_TextInfo_WordWrapEnabled() public void Parsing_TextInfo_WordWrapEnabled(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount) { m_TextComponent.text = testStrings[sourceTextIndex]; - m_TextComponent.enableWordWrapping = true; + m_TextComponent.textWrappingMode = TextWrappingModes.Normal; m_TextComponent.alignment = TextAlignmentOptions.TopLeft; // Size the RectTransform @@ -143,7 +142,7 @@ public static IEnumerable TestCases_Parsing_TextInfo_AlignmentTopJusti public void Parsing_TextInfo_AlignmentTopJustified(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount) { m_TextComponent.text = testStrings[sourceTextIndex]; - m_TextComponent.enableWordWrapping = true; + m_TextComponent.textWrappingMode = TextWrappingModes.Normal; m_TextComponent.alignment = TextAlignmentOptions.TopJustified; // Size the RectTransform @@ -169,7 +168,7 @@ public static IEnumerable TestCases_Parsing_TextInfo_RichText() public void Parsing_TextInfo_RichText(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount) { m_TextComponent.text = testStrings[sourceTextIndex]; - m_TextComponent.enableWordWrapping = true; + m_TextComponent.textWrappingMode = TextWrappingModes.Normal; m_TextComponent.alignment = TextAlignmentOptions.TopLeft; // Size the RectTransform diff --git a/ValidationExceptions.json b/ValidationExceptions.json deleted file mode 100644 index dca6edc..0000000 --- a/ValidationExceptions.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "ErrorExceptions": - [ - { - "ValidationTest": "API Validation", - "PackageVersion": "3.0.6" - } - ] -} \ No newline at end of file diff --git a/package.json b/package.json index 1b5b44f..d2003cb 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,9 @@ { "name": "com.unity.textmeshpro", "displayName": "TextMeshPro", - "version": "3.0.6", - "unity": "2020.1", - "unityRelease": "0a10", - "description": "TextMeshPro is the ultimate text solution for Unity. It's the perfect replacement for Unity's UI Text and the legacy Text Mesh.\n\nPowerful and easy to use, TextMeshPro (also known as TMP) uses Advanced Text Rendering techniques along with a set of custom shaders; delivering substantial visual quality improvements while giving users incredible flexibility when it comes to text styling and texturing.\n\nTextMeshPro provides Improved Control over text formatting and layout with features like character, word, line and paragraph spacing, kerning, justified text, Links, over 30 Rich Text Tags available, support for Multi Font & Sprites, Custom Styles and more.\n\nGreat performance. Since the geometry created by TextMeshPro uses two triangles per character just like Unity's text components, this improved visual quality and flexibility comes at no additional performance cost.", + "version": "3.2.0-pre.1", + "unity": "2020.3", + "description": "TextMeshPro is the ultimate text solution for Unity. It's the perfect replacement for Unity's UI Text and the legacy Text Mesh.\n\nPowerful and easy to use, TextMeshPro (also known as TMP) uses Advanced Text Rendering techniques along with a set of custom shaders; delivering substantial visual quality improvements while giving users incredible flexibility when it comes to text styling and texturing.\n\nTextMeshPro provides Improved Control over text formatting and layout with features like character, word, line and paragraph spacing, kerning, justified text, Links, over 30 Rich Text Tags available, support for Multi Font & Sprites, Custom Styles and more.\n\nGreat performance. Since the geometry created by TextMeshPro uses two triangles per character just like Unity's text components, this improved visual quality and flexibility comes at no additional performance cost.\n\n\n\nUPGRADE NOTE\n--------------------\nThis latest release of the TMP package includes updated TMP Essential Resources and TMP Examples & Extras. Be sure to update those via the \"Window - TextMeshPro - Import...\" menu options.", "keywords": [ "TextMeshPro", "TextMesh Pro", @@ -19,9 +18,9 @@ "repository": { "url": "https://github.cds.internal.unity3d.com/unity/com.unity.textmeshpro.git", "type": "git", - "revision": "01e57e3b7de18af9dfe9baaa0a0ff5cc4765c899" + "revision": "38d43e0ed4859fa888049f3a65fa5b6109210dbd" }, "upmCi": { - "footprint": "bfe9ad7192cdd29486fc5980b5ac7d0b3fafcec6" + "footprint": "ec24329e088da4547721e7cb0e1400c64b32b68f" } }