From a4fd2e5c5cf83f84d14c99c922e9cb7b7eff9d7d Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 15 May 2024 22:25:06 -0500 Subject: [PATCH] Version 0.10.0 Beta.2 [Prerelease] Second beta release of 0.10.0 Co-authored-by: Fribur --- .../BakingSystems/FontSmartBlobberSystem.cs | 53 +- .../LatiosTextBackendBakingUtility.cs | 6 +- .../Authoring/TextRendererAuthoring.cs | 24 +- Calligraphics/Components/FontBlob.cs | 24 +- Calligraphics/Components/TextComponents.cs | 50 +- Calligraphics/Internal/FixedStack512Bytes.cs | 1 + Calligraphics/Internal/GlyphGeneration.cs | 911 +++++++++++------- .../Internal/RichText/CalliExtensions.cs | 35 + .../Internal/RichText/RichTextParser.cs | 578 +++++------ Calligraphics/Internal/TextConfiguration.cs | 158 ++- .../Systems/AnimateTextTransitionSystem.cs | 16 +- Calligraphics/Systems/GenerateGlyphsSystem.cs | 4 +- .../GpuResidentTextDispatchSystem.cs | 65 +- .../Rendering/TextRenderingDispatchSystem.cs | 27 +- .../Rendering/TextRenderingUpdateSystem.cs | 17 +- .../Culling/GenerateBrgDrawCommandsSystem.cs | 504 +++++----- README.md | 4 +- .../UnityTransforms/TransformSuperSystems.cs | 3 - package.json | 2 +- 19 files changed, 1464 insertions(+), 1018 deletions(-) diff --git a/Calligraphics/Authoring/BakingSystems/FontSmartBlobberSystem.cs b/Calligraphics/Authoring/BakingSystems/FontSmartBlobberSystem.cs index 948ad0d..50944a0 100644 --- a/Calligraphics/Authoring/BakingSystems/FontSmartBlobberSystem.cs +++ b/Calligraphics/Authoring/BakingSystems/FontSmartBlobberSystem.cs @@ -113,12 +113,15 @@ public static unsafe BlobAssetReference BakeFont(FontAsset font, Mater fontBlobRoot.baseLine = font.faceInfo.baseline; fontBlobRoot.ascentLine = font.faceInfo.ascentLine; fontBlobRoot.descentLine = font.faceInfo.descentLine; + fontBlobRoot.capLine = font.faceInfo.capLine; + fontBlobRoot.meanLine = font.faceInfo.meanLine; fontBlobRoot.lineHeight = font.faceInfo.lineHeight; fontBlobRoot.subscriptOffset = font.faceInfo.subscriptOffset; fontBlobRoot.subscriptSize = font.faceInfo.subscriptSize; fontBlobRoot.superscriptOffset = font.faceInfo.superscriptOffset; fontBlobRoot.superscriptSize = font.faceInfo.superscriptSize; - fontBlobRoot.capLine = font.faceInfo.capLine; + fontBlobRoot.tabWidth = font.faceInfo.tabWidth; + fontBlobRoot.tabMultiple = font.tabMultiple; fontBlobRoot.regularStyleSpacing = font.regularStyleSpacing; fontBlobRoot.regularStyleWeight = font.regularStyleWeight; fontBlobRoot.boldStyleSpacing = font.boldStyleSpacing; @@ -135,16 +138,15 @@ public static unsafe BlobAssetReference BakeFont(FontAsset font, Mater hashCounts.Clear(); // Todo: Currently, we allocate a glyph per character and leave characters with null glyphs uninitialized. // We should rework that to only allocate glyphs to save memory. - BlobBuilderArray glyphBuilder = builder.Allocate(ref fontBlobRoot.characters, font.characterTable.Count); + var characterLookupTable = font.characterLookupTable; + BlobBuilderArray glyphBuilder = builder.Allocate(ref fontBlobRoot.characters, characterLookupTable.Count); BlobBuilderArray adjustmentPairs = builder.Allocate(ref fontBlobRoot.adjustmentPairs, glyphPairAdjustmentsSource.Count); - - var characterTable = font.characterTable; - + for (int i = 0; i < glyphPairAdjustmentsSource.Count; i++) { - var kerningPair = glyphPairAdjustmentsSource[i]; - if (GlyphIndexToUnicode(kerningPair.firstAdjustmentRecord.glyphIndex, characterTable, out int firstUnicode) && - GlyphIndexToUnicode(kerningPair.secondAdjustmentRecord.glyphIndex, characterTable, out int secondUnicode)) + var kerningPair = glyphPairAdjustmentsSource[i]; + if (GlyphIndexToUnicode(kerningPair.firstAdjustmentRecord.glyphIndex, characterLookupTable, out int firstUnicode) && + GlyphIndexToUnicode(kerningPair.secondAdjustmentRecord.glyphIndex, characterLookupTable, out int secondUnicode)) { adjustmentPairs[i] = new AdjustmentPair { @@ -152,32 +154,32 @@ public static unsafe BlobAssetReference BakeFont(FontAsset font, Mater { xPlacement = kerningPair.firstAdjustmentRecord.glyphValueRecord.xPlacement, yPlacement = kerningPair.firstAdjustmentRecord.glyphValueRecord.yPlacement, - xAdvance = kerningPair.firstAdjustmentRecord.glyphValueRecord.xAdvance, - yAdvance = kerningPair.firstAdjustmentRecord.glyphValueRecord.yAdvance, + xAdvance = kerningPair.firstAdjustmentRecord.glyphValueRecord.xAdvance, + yAdvance = kerningPair.firstAdjustmentRecord.glyphValueRecord.yAdvance, }, secondAdjustment = new GlyphAdjustment { xPlacement = kerningPair.secondAdjustmentRecord.glyphValueRecord.xPlacement, yPlacement = kerningPair.secondAdjustmentRecord.glyphValueRecord.yPlacement, - xAdvance = kerningPair.secondAdjustmentRecord.glyphValueRecord.xAdvance, - yAdvance = kerningPair.secondAdjustmentRecord.glyphValueRecord.yAdvance, + xAdvance = kerningPair.secondAdjustmentRecord.glyphValueRecord.xAdvance, + yAdvance = kerningPair.secondAdjustmentRecord.glyphValueRecord.yAdvance, }, fontFeatureLookupFlags = kerningPair.featureLookupFlags, - firstUnicode = firstUnicode, - secondUnicode = secondUnicode + firstUnicode = firstUnicode, + secondUnicode = secondUnicode }; } } - for (int i = 0; i < font.characterTable.Count; i++) - { - var character = font.characterTable[i]; - var glyph = character.glyph; + int characterCount = 0; + foreach (var character in characterLookupTable.Values) + { + var glyph = character.glyph; if (glyph == null) continue; var unicode = math.asint(character.unicode); - ref GlyphBlob glyphBlob = ref glyphBuilder[i]; + ref GlyphBlob glyphBlob = ref glyphBuilder[characterCount++]; glyphBlob.unicode = unicode; glyphBlob.glyphScale = glyph.scale; @@ -201,7 +203,7 @@ public static unsafe BlobAssetReference BakeFont(FontAsset font, Mater for (int j = 0; j < bk.Length; j++) { var d = adjustmentCacheBefore[j]; - bk[j] = d.x;//unicode + bk[j] = d.x; //unicode bv[j] = d.y; } adjustmentCacheAfter.Sort(new XSorter()); @@ -210,7 +212,7 @@ public static unsafe BlobAssetReference BakeFont(FontAsset font, Mater for (int j = 0; j < ak.Length; j++) { var d = adjustmentCacheAfter[j]; - ak[j] = d.x;//unicode + ak[j] = d.x; //unicode av[j] = d.y; } @@ -246,15 +248,14 @@ public static unsafe BlobAssetReference BakeFont(FontAsset font, Mater return result; } - static bool GlyphIndexToUnicode(uint glyphIndex, List characterTable, out int unicode) + static bool GlyphIndexToUnicode(uint glyphIndex, Dictionary characterLookupTable, out int unicode) { unicode = default; - for (int i = 0, end = characterTable.Count; i < end; i++) + foreach (var character in characterLookupTable.Values) { - var currentGlyphIndex = characterTable[i].glyphIndex; - if (currentGlyphIndex == glyphIndex) + if (character.glyphIndex == glyphIndex) { - unicode = math.asint(characterTable[i].unicode); + unicode = math.asint(character.unicode); return true; } } diff --git a/Calligraphics/Authoring/LatiosTextBackendBakingUtility.cs b/Calligraphics/Authoring/LatiosTextBackendBakingUtility.cs index dd86fcb..3bcb623 100644 --- a/Calligraphics/Authoring/LatiosTextBackendBakingUtility.cs +++ b/Calligraphics/Authoring/LatiosTextBackendBakingUtility.cs @@ -39,8 +39,7 @@ public static void BakeTextBackendMeshAndMaterial(this IBaker baker, Renderer re var entity = baker.GetEntity(TransformUsageFlags.Renderable); - baker.AddComponent(entity, new TextRenderControl { flags = TextRenderControl.Flags.Dirty }); - baker.AddBuffer(entity); + baker.AddComponent( entity, new TextRenderControl { flags = TextRenderControl.Flags.Dirty }); baker.AddComponent(entity); } @@ -50,8 +49,7 @@ public static void BakeTextBackendMeshAndMaterial(this IBaker baker, MeshRendere baker.BakeMeshAndMaterial(rendererSettings, mesh, material); - baker.AddComponent(rendererSettings.targetEntity, new TextRenderControl { flags = TextRenderControl.Flags.Dirty }); - baker.AddBuffer(rendererSettings.targetEntity); + baker.AddComponent( rendererSettings.targetEntity, new TextRenderControl { flags = TextRenderControl.Flags.Dirty }); baker.AddComponent(rendererSettings.targetEntity); } diff --git a/Calligraphics/Authoring/TextRendererAuthoring.cs b/Calligraphics/Authoring/TextRendererAuthoring.cs index 8e36d03..812c1b7 100644 --- a/Calligraphics/Authoring/TextRendererAuthoring.cs +++ b/Calligraphics/Authoring/TextRendererAuthoring.cs @@ -26,11 +26,17 @@ public class TextRendererAuthoring : MonoBehaviour public bool wordWrap = true; public float maxLineWidth = float.MaxValue; public HorizontalAlignmentOptions horizontalAlignment = HorizontalAlignmentOptions.Left; - public VerticalAlignmentOptions verticalAlignment = VerticalAlignmentOptions.Top; - public bool isOrthographic; - public bool enableKerning = true; - public FontStyles fontStyle; - public FontWeight fontWeight; + public VerticalAlignmentOptions verticalAlignment = VerticalAlignmentOptions.TopAscent; + public bool isOrthographic = false; + public bool enableKerning = true; + public FontStyles fontStyle = FontStyles.Normal; + public FontWeight fontWeight = FontWeight.Regular; + [Tooltip("Additional word spacing in font units where a value of 1 equals 1/100em.")] + public float wordSpacing = 0; + [Tooltip("Additional line spacing in font units where a value of 1 equals 1/100em.")] + public float lineSpacing = 0; + [Tooltip("Paragraph spacing in font units where a value of 1 equals 1/100em.")] + public float paragraphSpacing = 0; public Color32 color = UnityEngine.Color.white; @@ -61,6 +67,7 @@ public override void Bake(TextRendererAuthoring authoring) // Fonts and rendering AddFontRendering(entity, authoring.fontsAndMaterials[0]); + AddBuffer(entity); if (authoring.gpuResident) AddComponent(entity); @@ -68,10 +75,12 @@ public override void Bake(TextRendererAuthoring authoring) { AddComponent(entity); AddBuffer(entity); - AddBuffer(entity); + AddBuffer( entity); var additionalEntities = AddBuffer(entity).Reinterpret(); for (int i = 1; i < authoring.fontsAndMaterials.Count; i++) { + if (authoring.fontsAndMaterials[i].font == null) + continue; var newEntity = CreateAdditionalEntity(TransformUsageFlags.Renderable); AddFontRendering(newEntity, authoring.fontsAndMaterials[i]); AddComponent(newEntity); @@ -96,6 +105,9 @@ public override void Bake(TextRendererAuthoring authoring) enableKerning = authoring.enableKerning, fontStyle = authoring.fontStyle, fontWeight = authoring.fontWeight, + wordSpacing = authoring.wordSpacing, + lineSpacing = authoring.lineSpacing, + paragraphSpacing = authoring.paragraphSpacing, }); } diff --git a/Calligraphics/Components/FontBlob.cs b/Calligraphics/Components/FontBlob.cs index 2e02783..c7f3c29 100644 --- a/Calligraphics/Components/FontBlob.cs +++ b/Calligraphics/Components/FontBlob.cs @@ -15,13 +15,15 @@ public struct FontBlob public BlobArray characters; public BlobArray > glyphLookupMap; public BlobArray adjustmentPairs; + public float baseLine; public float ascentLine; public float descentLine; + public float capLine; + public float meanLine; public float lineHeight; public float pointSize; public float scale; - public float baseLine; public float atlasWidth; public float atlasHeight; @@ -31,24 +33,20 @@ public struct FontBlob public float boldStyleWeight; public byte italicsStyleSlant; - public float capLine; - public float subscriptOffset; public float subscriptSize; public float superscriptOffset; public float superscriptSize; + public float tabWidth; + public float tabMultiple; + //public float underlineOffset; /// /// Padding that is read from material properties /// public float materialPadding; - - public float baseScale => 1f / pointSize * scale * .1f; - - public const float smallCapsScaleMultiplier = .8f; - public const float orthographicScaleMultiplier = 10f; } public struct GlyphBlob @@ -64,13 +62,13 @@ public struct GlyphBlob public struct GlyphLookup { public int unicode; - public int index; + public int index; } public struct AdjustmentPair { - public int firstUnicode; - public int secondUnicode; + public int firstUnicode; + public int secondUnicode; public FontFeatureLookupFlags fontFeatureLookupFlags; public GlyphAdjustment firstAdjustment; public GlyphAdjustment secondAdjustment; @@ -94,9 +92,9 @@ public struct GlyphAdjustment public struct AdjustmentPairLookupByUnicode { - public BlobArray beforeKeys;//unicode + public BlobArray beforeKeys; //unicode public BlobArray beforeIndices; - public BlobArray afterKeys;//unicode + public BlobArray afterKeys; //unicode public BlobArray afterIndices; public unsafe bool TryGetAdjustmentPairIndexForUnicodeBefore(int otherUnicodeBefore, out int index) diff --git a/Calligraphics/Components/TextComponents.cs b/Calligraphics/Components/TextComponents.cs index b6fdd4b..a2eeb19 100644 --- a/Calligraphics/Components/TextComponents.cs +++ b/Calligraphics/Components/TextComponents.cs @@ -21,6 +21,9 @@ public struct FontBlobReference : IComponentData /// public struct TextBaseConfiguration : IComponentData { + // Todo: Current size is 28 bytes. We could make this 20 bytes by using half for + // fontSize, wordSpacing, lineSpacing, and paragraphSpacing. Worth it? + /// /// The size of the font, in font sizes /// @@ -33,26 +36,30 @@ public struct TextBaseConfiguration : IComponentData /// The vertex colors of the rendered text /// public Color32 color; + + public float wordSpacing; + public float lineSpacing; + public float paragraphSpacing; /// /// The horizontal alignment mode of the text /// public HorizontalAlignmentOptions lineJustification { - get => (HorizontalAlignmentOptions)((m_alignmentWeightOrtho & 0x70) >> 4); - set => m_alignmentWeightOrtho = (ushort)((m_alignmentWeightOrtho & ~0x70) | ((ushort)value << 4)); + get => (HorizontalAlignmentOptions)((m_alignmentWeightOrthoKerning & 0x70) >> 4); + set => m_alignmentWeightOrthoKerning = (ushort)((m_alignmentWeightOrthoKerning & ~0x70) | ((ushort)value << 4)); } /// /// The vertical alignment mode of the text /// public VerticalAlignmentOptions verticalAlignment { - get => (VerticalAlignmentOptions)((m_alignmentWeightOrtho & 0x380) >> 7); - set => m_alignmentWeightOrtho = (ushort)((m_alignmentWeightOrtho & ~0x380) | ((ushort)value << 7)); + get => (VerticalAlignmentOptions)((m_alignmentWeightOrthoKerning & 0x780) >> 7); + set => m_alignmentWeightOrthoKerning = (ushort)((m_alignmentWeightOrthoKerning & ~0x780) | ((ushort)value << 7)); } public FontWeight fontWeight { - get => (FontWeight)(m_alignmentWeightOrtho & 0x0f); - set => m_alignmentWeightOrtho = (ushort)((m_alignmentWeightOrtho & ~0x0f) | (ushort)value); + get => (FontWeight)(m_alignmentWeightOrthoKerning & 0x0f); + set => m_alignmentWeightOrthoKerning = (ushort)((m_alignmentWeightOrthoKerning & ~0x0f) | (ushort)value); } public FontStyles fontStyle { @@ -61,13 +68,17 @@ public FontStyles fontStyle } public bool isOrthographic { - get => (m_alignmentWeightOrtho & 0x8000) != 0; - set => m_alignmentWeightOrtho = (ushort)((m_alignmentWeightOrtho & 0x7fff) | (value ? 0x8000 : 0)); + get => (m_alignmentWeightOrthoKerning & 0x8000) != 0; + set => m_alignmentWeightOrthoKerning = (ushort)((m_alignmentWeightOrthoKerning & 0x7fff) | (value ? 0x8000 : 0)); + } + public bool enableKerning + { + get => (m_alignmentWeightOrthoKerning & 0x4000) != 0; + set => m_alignmentWeightOrthoKerning = (ushort)((m_alignmentWeightOrthoKerning & 0xbfff) | (value ? 0x4000 : 0)); } - public bool enableKerning; - private ushort m_fontStyleFlags; // 6 bits unused, but Unity may add more. - ushort m_alignmentWeightOrtho; // 5 bits unused. + ushort m_fontStyleFlags; // 6 bits unused, but Unity may add more. + ushort m_alignmentWeightOrthoKerning; // 3 bits unused. } /// @@ -130,12 +141,17 @@ public enum HorizontalAlignmentOptions : byte /// public enum VerticalAlignmentOptions : byte { - Top, - Middle, - Bottom, - Baseline, - Geometry, - Capline, + TopBase, + TopAscent, + TopDescent, + TopCap, + TopMean, + BottomBase, + BottomAscent, + BottomDescent, + BottomCap, + BottomMean, + MiddleTopAscentToBottomDescent, } public enum FontWeight diff --git a/Calligraphics/Internal/FixedStack512Bytes.cs b/Calligraphics/Internal/FixedStack512Bytes.cs index bd17706..a8e6443 100644 --- a/Calligraphics/Internal/FixedStack512Bytes.cs +++ b/Calligraphics/Internal/FixedStack512Bytes.cs @@ -25,6 +25,7 @@ public T RemoveExceptRoot() Pop(); return Peek(); } + public void Clear() => m_buffer.Clear(); } } diff --git a/Calligraphics/Internal/GlyphGeneration.cs b/Calligraphics/Internal/GlyphGeneration.cs index 39de72a..3d33916 100644 --- a/Calligraphics/Internal/GlyphGeneration.cs +++ b/Calligraphics/Internal/GlyphGeneration.cs @@ -1,4 +1,3 @@ -using System.Runtime.CompilerServices; using Latios.Calligraphics.Rendering; using Latios.Calligraphics.RichText; using Unity.Collections; @@ -6,103 +5,385 @@ using Unity.Entities; using Unity.Mathematics; using UnityEngine; -using UnityEngine.TextCore; using UnityEngine.TextCore.LowLevel; using UnityEngine.TextCore.Text; -using UnityEngine.UIElements; namespace Latios.Calligraphics { internal static class GlyphGeneration { + /// This function logic follows TMPro_Private.GenerateTextMesh() internal static unsafe void CreateRenderGlyphs(ref DynamicBuffer renderGlyphs, ref GlyphMappingWriter mappingWriter, ref FontMaterialSet fontMaterialSet, + ref TextConfigurationStack textConfigurationStack, in DynamicBuffer calliBytes, in TextBaseConfiguration baseConfiguration) { renderGlyphs.Clear(); - //initialized textConfiguration which stores all fields that are modified by RichText Tags - var richTextTagIdentifiers = new FixedList512Bytes(); - var textConfiguration = new TextConfiguration(baseConfiguration); + // Initialize textConfiguration which stores all fields that are modified by RichText Tags + textConfigurationStack.Reset(baseConfiguration); + var calliString = new CalliString(calliBytes); - float2 cumulativeOffset = new float2(); // Tracks text progression and word wrap - float2 adjustmentOffset = new float2(); //Tracks placement adjustments + var textConfiguration = textConfigurationStack.GetActiveConfiguration(); + ref FontBlob font = ref fontMaterialSet[textConfiguration.m_currentFontMaterialIndex]; + + int characterCount = 0; int lastWordStartCharacterGlyphIndex = 0; FixedList512Bytes characterGlyphIndicesWithPreceedingSpacesInLine = default; int accumulatedSpaces = 0; int startOfLineGlyphIndex = 0; - bool prevWasSpace = false; + int lastCommittedStartOfLineGlyphIndex = -1; int lineCount = 0; bool isLineStart = true; - ref FontBlob font = ref fontMaterialSet[textConfiguration.m_currentFontMaterialIndex]; - var calliString = new CalliString(calliBytes); - var characterEnumerator = calliString.GetEnumerator(); - while (characterEnumerator.MoveNext()) + float currentLineHeight = 0f; + float ascentLineDelta = 0; + float decentLineDelta = 0; + float accumulatedVerticalOffset = 0f; + float maxLineAscender = float.MinValue; + float maxLineDescender = float.MaxValue; + float xAdvance = 0f; + + // Calculate the scale of the font based on selected font size and sampling point size. + // baseScale is calculated using the font asset assigned to the text object. + float baseScale = baseConfiguration.fontSize / font.pointSize * font.scale * (baseConfiguration.isOrthographic ? 1 : 0.1f); + float currentElementScale = baseScale; + float currentEmScale = baseConfiguration.fontSize * 0.01f * (baseConfiguration.isOrthographic ? 1 : 0.1f); + + float topAnchor = GetTopAnchorForConfig(ref fontMaterialSet[0], baseConfiguration.verticalAlignment, baseScale); + float bottomAnchor = GetBottomAnchorForConfig(ref fontMaterialSet[0], baseConfiguration.verticalAlignment, baseScale); + + PrevCurNext prevCurNext = new PrevCurNext(calliString.GetEnumerator()); + TextGenerationStateCommands textGenerationStateCommands = default; + textGenerationStateCommands.Reset(); + bool needsActiveConfigurationUpdate = false; + bool firstIteration = true; + + while (prevCurNext.nextIsValid) { - var currentRune = characterEnumerator.Current; - textConfiguration.m_characterCount++; + // On the first iteration, we need to queue up the first character, which could be rich text. + // So we process the first character, do rich text analysis, then skip to the second iteration + // where we shift everything. Because from the second iteration on, we want to do rich text parsing + // after advancing the prevCurNext. + if (!firstIteration) + { + // We update the count now so that RichTextParser receives the actual character count. + characterCount++; + prevCurNext.MoveNext(); + } + + // If on the previous iteration we detected a new Html tag, we need to update the active configuration. + if (needsActiveConfigurationUpdate) + { + textConfiguration = textConfigurationStack.GetActiveConfiguration(); + if (textGenerationStateCommands.xAdvanceIsOverwrite) + xAdvance = textGenerationStateCommands.xAdvanceChange; + else + xAdvance += textGenerationStateCommands.xAdvanceChange; + textGenerationStateCommands.Reset(); + needsActiveConfigurationUpdate = false; + } - // Parse Rich Text Tag - #region Parse Rich Text Tag - if (currentRune == '<') // '<' + while (prevCurNext.nextIsValid && prevCurNext.next.Current == '<') { - textConfiguration.m_isParsingText = true; + textConfigurationStack.m_isParsingText = true; // Check if Tag is valid. If valid, skip to the end of the validated tag. - if (RichTextParser.ValidateHtmlTag(in calliString, ref characterEnumerator, ref fontMaterialSet, in baseConfiguration, ref textConfiguration, - ref richTextTagIdentifiers)) + var nextEnumerator = prevCurNext.next; + if (RichTextParser.ValidateHtmlTag(in calliString, ref nextEnumerator, ref fontMaterialSet, in baseConfiguration, ref textConfigurationStack, + ref textGenerationStateCommands, characterCount)) { + prevCurNext.next = nextEnumerator; + needsActiveConfigurationUpdate = true; // Continue to next character + prevCurNext.nextIsValid = prevCurNext.next.MoveNext(); continue; } + // We failed to parse something. We want to render the '<' instead. + break; } - #endregion - font = ref fontMaterialSet[textConfiguration.m_currentFontMaterialIndex]; - textConfiguration.m_isParsingText = false; + if (firstIteration) + { + // Commit the first line to the writer + mappingWriter.AddLineStart(renderGlyphs.Length); + mappingWriter.AddWordStart(renderGlyphs.Length); + firstIteration = false; + continue; + } + + var currentRune = prevCurNext.current.Current; + font = ref fontMaterialSet[textConfiguration.m_currentFontMaterialIndex]; + textConfigurationStack.m_isParsingText = false; + if (lineCount == 0) + topAnchor = GetTopAnchorForConfig(ref font, baseConfiguration.verticalAlignment, baseScale, topAnchor); + bottomAnchor = GetBottomAnchorForConfig(ref font, baseConfiguration.verticalAlignment, baseScale, bottomAnchor); + + bool prevWasSpace = prevCurNext.previousIsValid && prevCurNext.prev.Current.value == 32; // Handle Font Styles like LowerCase, UpperCase and SmallCaps. - #region Handling of LowerCase, UpperCase and SmallCaps Font Styles + SwapRune(ref currentRune, ref textConfiguration, out float smallCapsMultiplier); + + // Look up Character Data. TMP uses a backing array, + // we pull character directly from FontBlob and continue when not found + #region Look up Character Data + if (!font.TryGetCharacterIndex(currentRune, out var currentCharIndex)) + continue; + + ref var glyphBlob = ref font.characters[currentCharIndex]; + + float adjustedScale = textConfiguration.m_currentFontSize * smallCapsMultiplier / font.pointSize * font.scale * (baseConfiguration.isOrthographic ? 1 : 0.1f); + float elementAscentLine = font.ascentLine; + float elementDescentLine = font.descentLine; + + currentElementScale = adjustedScale * textConfiguration.m_fontScaleMultiplier * glyphBlob.glyphScale; //* m_cached_TextElement.m_Scale + float baselineOffset = font.baseLine * adjustedScale * textConfiguration.m_fontScaleMultiplier * font.scale; + #endregion + + // Handle Soft Hyphen + #region Handle Soft Hyphen + float currentElementUnmodifiedScale = currentElementScale; + if (currentRune.value == 0xAD || currentRune.value == 0x03) + currentElementScale = 0; + #endregion - float smallCapsMultiplier = 1.0f; + // Cache glyph metrics + var currentGlyphMetrics = glyphBlob.glyphMetrics; - // Todo: Burst does not support language methods, and char only supports the UTF-16 subset - // of characters. We should encode upper and lower cross-references into the font blobs or - // figure out the formulas for all other languages. Right now only ascii is supported. - if ((textConfiguration.m_fontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase) + // Optimization to avoid calling this more than once per character. + bool isWhiteSpace = currentRune.value <= 0xFFFF && currentRune.IsWhiteSpace(); + + // Handle Kerning if Enabled. + #region Handle Kerning + GlyphAdjustment glyphAdjustments = new(); + float characterSpacingAdjustment = 0; //consider exposing initial characterSpacing in TextRenderer as characterSpacing; + if (baseConfiguration.enableKerning) { - // If this character is lowercase, switch to uppercase. - currentRune = currentRune.ToUpper(); + // Todo: If the active configuration changes between glyphs, we may want to cancel out of kerning. + if (prevCurNext.nextIsValid) + { + var nextRune = prevCurNext.next.Current; + SwapRune(ref nextRune, ref textConfiguration, out _); + if (glyphBlob.glyphAdjustmentsLookup.TryGetAdjustmentPairIndexForUnicodeAfter(nextRune.value, out var adjustmentIndex)) + { + var adjustmentPair = font.adjustmentPairs[adjustmentIndex]; + glyphAdjustments = adjustmentPair.firstAdjustment; + characterSpacingAdjustment = (adjustmentPair.fontFeatureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == + FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; + } + } + + if (prevCurNext.previousIsValid) + { + var prevRune = prevCurNext.prev.Current; + SwapRune(ref prevRune, ref textConfiguration, out _); + if (glyphBlob.glyphAdjustmentsLookup.TryGetAdjustmentPairIndexForUnicodeBefore(prevRune.value, out var adjustmentIndex)) + { + var adjustmentPair = font.adjustmentPairs[adjustmentIndex]; + glyphAdjustments += adjustmentPair.secondAdjustment; + characterSpacingAdjustment = (adjustmentPair.fontFeatureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == + FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; + } + } } - else if ((textConfiguration.m_fontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase) + #endregion + + // Handle Mono Spacing + #region Handle Mono Spacing + float monoAdvance = 0; + if (textConfiguration.m_monoSpacing != 0) { - // If this character is uppercase, switch to lowercase. - currentRune = currentRune.ToLower(); + monoAdvance = + (textConfiguration.m_monoSpacing / 2 - (currentGlyphMetrics.width / 2 + currentGlyphMetrics.horizontalBearingX) * currentElementScale); // * (1 - charWidthAdjDelta); + xAdvance += monoAdvance; } - else if ((textConfiguration.m_fontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps) + #endregion + + // Set Padding based on selected font style + #region Handle Style Padding + float boldSpacingAdjustment = 0; + float style_padding = 0; + if ((textConfiguration.m_fontStyleInternal & FontStyles.Bold) == FontStyles.Bold) { - var oldUnicode = currentRune; - currentRune = currentRune.ToUpper(); - if (currentRune != oldUnicode) - { - smallCapsMultiplier = 0.8f; - } + style_padding = 0; + boldSpacingAdjustment = font.boldStyleSpacing; } + #endregion Handle Style Padding + + // Determine the position of the vertices of the Character or Sprite. + #region Calculate Vertices Position + var renderGlyph = new RenderGlyph { unicode = glyphBlob.unicode }; + + // top left is used to position bottom left and top right + float2 topLeft; + topLeft.x = xAdvance + + ((currentGlyphMetrics.horizontalBearingX * textConfiguration.m_fxScale.x - font.materialPadding - style_padding + glyphAdjustments.xPlacement) * + currentElementScale); // * (1 - m_charWidthAdjDelta)); + topLeft.y = baselineOffset + (currentGlyphMetrics.horizontalBearingY + font.materialPadding + glyphAdjustments.yPlacement) * currentElementScale - + textConfiguration.m_lineOffset + textConfiguration.m_baselineOffset; + + float2 bottomLeft; + bottomLeft.x = topLeft.x; + bottomLeft.y = topLeft.y - ((currentGlyphMetrics.height + font.materialPadding * 2) * currentElementScale); + + float2 topRight; + topRight.x = bottomLeft.x + (currentGlyphMetrics.width * textConfiguration.m_fxScale.x + font.materialPadding * 2 + style_padding * 2) * currentElementScale; + topRight.y = topLeft.y; + + // Bottom right unused #endregion - if (isLineStart) + #region Setup UVA + var glyphRect = glyphBlob.glyphRect; + float2 blUVA, tlUVA, trUVA, brUVA; + blUVA.x = (glyphRect.x - font.materialPadding - style_padding) / font.atlasWidth; + blUVA.y = (glyphRect.y - font.materialPadding - style_padding) / font.atlasHeight; + + tlUVA.x = blUVA.x; + tlUVA.y = (glyphRect.y + font.materialPadding + style_padding + glyphRect.height) / font.atlasHeight; + + trUVA.x = (glyphRect.x + font.materialPadding + style_padding + glyphRect.width) / font.atlasWidth; + trUVA.y = tlUVA.y; + + brUVA.x = trUVA.x; + brUVA.y = blUVA.y; + + renderGlyph.blUVA = blUVA; + renderGlyph.trUVA = trUVA; + #endregion + + #region Setup UVB + //Setup UV2 based on Character Mapping Options Selected + //m_horizontalMapping case TextureMappingOptions.Character + float2 blUVC, tlUVC, trUVC, brUVC; + blUVC.x = 0; + tlUVC.x = 0; + trUVC.x = 1; + brUVC.x = 1; + + //m_verticalMapping case case TextureMappingOptions.Character + blUVC.y = 0; + tlUVC.y = 1; + trUVC.y = 1; + brUVC.y = 0; + + renderGlyph.blUVB = blUVC; + renderGlyph.tlUVB = tlUVA; + renderGlyph.trUVB = trUVC; + renderGlyph.brUVB = brUVA; + #endregion + + #region Setup Color + renderGlyph.blColor = textConfiguration.m_htmlColor; + renderGlyph.tlColor = textConfiguration.m_htmlColor; + renderGlyph.trColor = textConfiguration.m_htmlColor; + renderGlyph.brColor = textConfiguration.m_htmlColor; + #endregion + + #region Pack Scale into renderGlyph.scale + var xScale = textConfiguration.m_currentFontSize; // * math.abs(lossyScale) * (1 - m_charWidthAdjDelta); + if ((textConfiguration.m_fontStyleInternal & FontStyles.Bold) == FontStyles.Bold) + xScale *= -1; + + renderGlyph.scale = xScale; + #endregion + + // Check if we need to Shear the rectangles for Italic styles + #region Handle Italic & Shearing + float bottomShear = 0f; + if ((textConfiguration.m_fontStyleInternal & FontStyles.Italic) == FontStyles.Italic) { - isLineStart = false; - mappingWriter.AddLineStart(renderGlyphs.Length); - if (!prevWasSpace) + // Shift Top vertices forward by half (Shear Value * height of character) and Bottom vertices back by same amount. + float shear_value = textConfiguration.m_italicAngle * 0.01f; + float midPoint = ((font.capLine - (font.baseLine + textConfiguration.m_baselineOffset)) / 2) * textConfiguration.m_fontScaleMultiplier * font.scale; + float topShear = shear_value * ((currentGlyphMetrics.horizontalBearingY + font.materialPadding + style_padding - midPoint) * currentElementScale); + bottomShear = shear_value * + ((currentGlyphMetrics.horizontalBearingY - currentGlyphMetrics.height - font.materialPadding - style_padding - midPoint) * + currentElementScale); + + topLeft.x += topShear; + bottomLeft.x += bottomShear; + topRight.x += topShear; + + renderGlyph.shear = topLeft.x - bottomLeft.x; + } + #endregion Handle Italics & Shearing + + // Handle Character FX Rotation + #region Handle Character FX Rotation + renderGlyph.rotationCCW = textConfiguration.m_fxRotationAngleCCW; + #endregion + + #region Store vertex information for the character or sprite. + renderGlyph.trPosition = topRight; + renderGlyph.blPosition = bottomLeft; + renderGlyphs.Add(renderGlyph); + fontMaterialSet.WriteFontMaterialIndexForGlyph(textConfiguration.m_currentFontMaterialIndex); + mappingWriter.AddCharNoTags(characterCount - 1, true); + mappingWriter.AddCharWithTags(prevCurNext.current.CurrentCharIndex, true); + mappingWriter.AddBytes(prevCurNext.current.CurrentByteIndex, currentRune.LengthInUtf8Bytes(), true); + #endregion + + // Compute text metrics + #region Compute Ascender & Descender values + // Element Ascender in line space + float elementAscender = elementAscentLine * currentElementScale / smallCapsMultiplier + textConfiguration.m_baselineOffset; + + // Element Descender in line space + float elementDescender = elementDescentLine * currentElementScale / smallCapsMultiplier + textConfiguration.m_baselineOffset; + + float adjustedAscender = elementAscender; + float adjustedDescender = elementDescender; + + // Max line ascender and descender in line space + if (isLineStart || isWhiteSpace == false) + { + // Special handling for Superscript and Subscript where we use the unadjusted line ascender and descender + if (textConfiguration.m_baselineOffset != 0) { - mappingWriter.AddWordStart(renderGlyphs.Length); + adjustedAscender = math.max((elementAscender - textConfiguration.m_baselineOffset) / textConfiguration.m_fontScaleMultiplier, adjustedAscender); + adjustedDescender = math.min((elementDescender - textConfiguration.m_baselineOffset) / textConfiguration.m_fontScaleMultiplier, adjustedDescender); } + maxLineAscender = math.max(adjustedAscender, maxLineAscender); + maxLineDescender = math.min(adjustedDescender, maxLineDescender); + } + #endregion + + // Handle xAdvance & Tabulation Stops. Tab stops at every 25% of Font Size. + #region XAdvance, Tabulation & Stops + if (currentRune.value == 9) + { + float tabSize = font.tabWidth * font.tabMultiple * currentElementScale; + float tabs = math.ceil(xAdvance / tabSize) * tabSize; + xAdvance = tabs > xAdvance ? tabs : xAdvance + tabSize; } + else if (textConfiguration.m_monoSpacing != 0) + { + float monoAdjustment = textConfiguration.m_monoSpacing - monoAdvance; + xAdvance += (monoAdjustment + ((font.regularStyleSpacing + characterSpacingAdjustment) * currentEmScale) + textConfiguration.m_cSpacing); // * (1 - m_charWidthAdjDelta); + if (isWhiteSpace || currentRune.value == 0x200B) + xAdvance += baseConfiguration.wordSpacing * currentEmScale; + } + else + { + xAdvance += + ((currentGlyphMetrics.horizontalAdvance * textConfiguration.m_fxScale.x + glyphAdjustments.xAdvance) * currentElementScale + + (font.regularStyleSpacing + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + textConfiguration.m_cSpacing); // * (1 - m_charWidthAdjDelta); + + if (isWhiteSpace || currentRune.value == 0x200B) + xAdvance += baseConfiguration.wordSpacing * currentEmScale; + } + #endregion XAdvance, Tabulation & Stops - //Handle line break - if (currentRune.value == 10) //Line feed + #region Check for Line Feed and Last Character + if (isLineStart) + isLineStart = false; + currentLineHeight = font.lineHeight * baseScale; //why not (font.ascentLine-font.baseLine) * baseScale ? + ascentLineDelta = maxLineAscender - font.ascentLine * baseScale; + decentLineDelta = font.descentLine * baseScale - maxLineDescender; + //if (currentRune.value == 10 || currentRune.value == 11 || currentRune.value == 0x03 || currentRune.value == 0x2028 || + // currentRune.value == 0x2029 || textConfiguration.m_characterCount == calliString.Length - 1) + if (currentRune.value == 10) { var glyphsLine = renderGlyphs.AsNativeArray().GetSubArray(startOfLineGlyphIndex, renderGlyphs.Length - startOfLineGlyphIndex); var overrideMode = textConfiguration.m_lineJustification; @@ -115,300 +396,207 @@ internal static unsafe void CreateRenderGlyphs(ref DynamicBuffer re ref characterGlyphIndicesWithPreceedingSpacesInLine, baseConfiguration.maxLineWidth, overrideMode); - lineCount++; - isLineStart = true; - - cumulativeOffset.x = 0; - cumulativeOffset.y -= font.lineHeight * font.baseScale * baseConfiguration.fontSize; - continue; - } - - if (font.TryGetCharacterIndex(currentRune, out var currentCharIndex)) - { - ref var glyphBlob = ref font.characters[currentCharIndex]; - var renderGlyph = new RenderGlyph - { - unicode = glyphBlob.unicode, - blColor = textConfiguration.m_htmlColor, - tlColor = textConfiguration.m_htmlColor, - trColor = textConfiguration.m_htmlColor, - brColor = textConfiguration.m_htmlColor, - }; - - // Set Padding based on selected font style - #region Handle Style Padding - float boldSpacingAdjustment = 0; - float style_padding = 0; - if ((textConfiguration.m_fontStyleInternal & FontStyles.Bold) == FontStyles.Bold) - { - style_padding = 0; - boldSpacingAdjustment = font.boldStyleSpacing; - } - #endregion Handle Style Padding - - var adjustedScale = textConfiguration.m_currentFontSize * smallCapsMultiplier / font.pointSize * font.scale * - (baseConfiguration.isOrthographic ? 1 : 0.1f); - var currentElementScale = adjustedScale * textConfiguration.m_fontScaleMultiplier * glyphBlob.glyphScale; //* m_cached_TextElement.m_Scale - float currentEmScale = baseConfiguration.fontSize * 0.01f * (baseConfiguration.isOrthographic ? 1 : 0.1f); - - // Determine the position of the vertices of the Character - #region Calculate Vertices Position - var currentGlyphMetrics = glyphBlob.glyphMetrics; - float2 topLeft; - topLeft.x = (currentGlyphMetrics.horizontalBearingX * textConfiguration.m_FXScale.x - font.materialPadding - style_padding) * currentElementScale; - topLeft.y = (currentGlyphMetrics.horizontalBearingY + font.materialPadding) * currentElementScale - textConfiguration.m_lineOffset + - textConfiguration.m_baselineOffset; - - float2 bottomLeft; - bottomLeft.x = topLeft.x; - bottomLeft.y = topLeft.y - ((currentGlyphMetrics.height + font.materialPadding * 2) * currentElementScale); - - float2 topRight; - topRight.x = bottomLeft.x + (currentGlyphMetrics.width * textConfiguration.m_FXScale.x + font.materialPadding * 2 + style_padding * 2) * currentElementScale; - topRight.y = topLeft.y; - - float2 bottomRight; - bottomRight.x = topRight.x; - bottomRight.y = bottomLeft.y; - #endregion - - #region Setup UVA - var glyphRect = glyphBlob.glyphRect; - float2 blUVA, tlUVA, trUVA, brUVA; - blUVA.x = (glyphRect.x - font.materialPadding - style_padding) / font.atlasWidth; - blUVA.y = (glyphRect.y - font.materialPadding - style_padding) / font.atlasHeight; - - tlUVA.x = blUVA.x; - tlUVA.y = (glyphRect.y + font.materialPadding + style_padding + glyphRect.height) / font.atlasHeight; - - trUVA.x = (glyphRect.x + font.materialPadding + style_padding + glyphRect.width) / font.atlasWidth; - trUVA.y = tlUVA.y; - - brUVA.x = trUVA.x; - brUVA.y = blUVA.y; - - renderGlyph.blUVA = blUVA; - renderGlyph.trUVA = trUVA; - #endregion - - #region Setup UVB - //Setup UV2 based on Character Mapping Options Selected - //m_horizontalMapping case TextureMappingOptions.Character - float2 blUVC, tlUVC, trUVC, brUVC; - blUVC.x = 0; - tlUVC.x = 0; - trUVC.x = 1; - brUVC.x = 1; - - //m_verticalMapping case case TextureMappingOptions.Character - blUVC.y = 0; - tlUVC.y = 1; - trUVC.y = 1; - brUVC.y = 0; - - renderGlyph.blUVB = blUVC; - renderGlyph.tlUVB = tlUVA; - renderGlyph.trUVB = trUVC; - renderGlyph.brUVB = brUVA; - #endregion - - // Check if we need to Shear the rectangles for Italic styles - #region Handle Italic & Shearing - if (((textConfiguration.m_fontStyleInternal & FontStyles.Italic) == FontStyles.Italic)) - { - // Shift Top vertices forward by half (Shear Value * height of character) and Bottom vertices back by same amount. - float shear = textConfiguration.m_italicAngle * 0.01f; - float2 topShear = new float2(shear * ((currentGlyphMetrics.horizontalBearingY + font.materialPadding + style_padding) * currentElementScale), 0); - float2 bottomShear = - new float2( - shear * (((currentGlyphMetrics.horizontalBearingY - currentGlyphMetrics.height - font.materialPadding - style_padding)) * currentElementScale), - 0); - float2 shearAdjustment = (topShear - bottomShear) * 0.5f; - - topShear -= shearAdjustment; - bottomShear -= shearAdjustment; - - topLeft += topShear; - bottomLeft += bottomShear; - topRight += topShear; - bottomRight += bottomShear; - - renderGlyph.shear = (topLeft.x - bottomLeft.x); - } - #endregion Handle Italics & Shearing - - // Handle Character FX Rotation - #region Handle Character FX Rotation - renderGlyph.rotationCCW = textConfiguration.m_FXRotationAngle; - #endregion - - #region handle bold - var xScale = textConfiguration.m_currentFontSize; // * math.abs(lossyScale) * (1 - m_charWidthAdjDelta); - if ((textConfiguration.m_fontStyleInternal & FontStyles.Bold) == FontStyles.Bold) - xScale *= -1; - - renderGlyph.scale = xScale; - #endregion - - #region apply offsets - var offset = adjustmentOffset + cumulativeOffset; - topLeft += offset; - bottomLeft += offset; - topRight += offset; - bottomRight += offset; - #endregion - - renderGlyph.trPosition = topRight; - renderGlyph.blPosition = bottomLeft; - - renderGlyphs.Add(renderGlyph); - fontMaterialSet.WriteFontMaterialIndexForGlyph(textConfiguration.m_currentFontMaterialIndex); - mappingWriter.AddCharNoTags(textConfiguration.m_characterCount - 1, true); - mappingWriter.AddCharWithTags(characterEnumerator.CurrentCharIndex, true); - mappingWriter.AddBytes(characterEnumerator.CurrentByteIndex, currentRune.LengthInUtf8Bytes(), true); - - // Handle Kerning if Enabled. - #region Handle Kerning - adjustmentOffset = float2.zero; - float m_characterSpacing = 0; - GlyphAdjustment glyphAdjustments = new(); - float characterSpacingAdjustment = m_characterSpacing; - float m_GlyphHorizontalAdvanceAdjustment = 0; - if (baseConfiguration.enableKerning) + startOfLineGlyphIndex = renderGlyphs.Length; + if (lineCount > 0) { - if (characterEnumerator.MoveNext()) - { - var nextUnicodeRune = characterEnumerator.Current; - if (glyphBlob.glyphAdjustmentsLookup.TryGetAdjustmentPairIndexForUnicodeAfter(nextUnicodeRune.value, out var adjustmentIndex)) - { - var adjustmentPair = font.adjustmentPairs[adjustmentIndex]; - glyphAdjustments = adjustmentPair.firstAdjustment; - characterSpacingAdjustment = (adjustmentPair.fontFeatureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == - FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; - } - characterEnumerator.MovePrevious(); //rewind - } - - if (textConfiguration.m_characterCount >= 1) + accumulatedVerticalOffset += currentLineHeight + ascentLineDelta; + if (lastCommittedStartOfLineGlyphIndex != startOfLineGlyphIndex) { - characterEnumerator.MovePrevious(); - var prevUnicodeRune = characterEnumerator.Current; - if (glyphBlob.glyphAdjustmentsLookup.TryGetAdjustmentPairIndexForUnicodeBefore(prevUnicodeRune.value, out var adjustmentIndex)) - { - var adjustmentPair = font.adjustmentPairs[adjustmentIndex]; - glyphAdjustments += adjustmentPair.secondAdjustment; - characterSpacingAdjustment = (adjustmentPair.fontFeatureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == - FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment; - } - characterEnumerator.MoveNext(); //undo rewind + ApplyVerticalOffsetToGlyphs(ref glyphsLine, accumulatedVerticalOffset); + lastCommittedStartOfLineGlyphIndex = startOfLineGlyphIndex; } } + accumulatedVerticalOffset += decentLineDelta; + //apply user configurable line and paragraph spacing + accumulatedVerticalOffset += + (baseConfiguration.lineSpacing + (currentRune.value == 10 || currentRune.value == 0x2029 ? baseConfiguration.paragraphSpacing : 0)) * currentEmScale; + //reset line status + maxLineAscender = float.MinValue; + maxLineDescender = float.MaxValue; - m_GlyphHorizontalAdvanceAdjustment = glyphAdjustments.xAdvance; + lineCount++; + isLineStart = true; + bottomAnchor = GetBottomAnchorForConfig(ref font, baseConfiguration.verticalAlignment, baseScale); - adjustmentOffset.x = glyphAdjustments.xPlacement * currentElementScale; - adjustmentOffset.y = glyphAdjustments.yPlacement * currentElementScale; + // Commit the line to the writer + mappingWriter.AddLineStart(renderGlyphs.Length); - cumulativeOffset.x += - ((currentGlyphMetrics.horizontalAdvance * textConfiguration.m_FXScale.x + glyphAdjustments.xAdvance) * currentElementScale + - (font.regularStyleSpacing + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + textConfiguration.m_cSpacing); // * (1 - m_charWidthAdjDelta); - cumulativeOffset.y += glyphAdjustments.yAdvance * currentElementScale; - #endregion + xAdvance = 0; + } + #endregion - #region Word Wrapping - // Apply accumulated spaces to non-space character - while (currentRune.value != 32 && accumulatedSpaces > 0) + #region Word Wrapping + // Handle word wrap + if (currentRune.value != 10 && + baseConfiguration.maxLineWidth < float.MaxValue && + baseConfiguration.maxLineWidth > 0 && + xAdvance > baseConfiguration.maxLineWidth) + { + bool dropSpace = false; + if (currentRune.value == 32 && !prevWasSpace) { - // We add the glyph entry for each proceeding whitespace, so that the justified offset is - // "weighted" by the preceeding number of spaces. - characterGlyphIndicesWithPreceedingSpacesInLine.Add(renderGlyphs.Length - 1 - startOfLineGlyphIndex); + // What pushed us past the line width was a space character. + // The previous character was not a space, and we don't + // want to render this character at the start of the next line. + // We drop this space character instead and allow the next + // character to line-wrap, space or not. + dropSpace = true; accumulatedSpaces--; } - // Handle word wrap - if (baseConfiguration.maxLineWidth < float.MaxValue && - baseConfiguration.maxLineWidth > 0 && - cumulativeOffset.x > baseConfiguration.maxLineWidth) + var yOffsetChange = 0f; //font.lineHeight * currentElementScale; + var xOffsetChange = renderGlyphs[lastWordStartCharacterGlyphIndex].blPosition.x - bottomShear; + if (xOffsetChange > 0 && !dropSpace) // Always allow one visible character { - bool dropSpace = false; - if (currentRune.value == 32 && !prevWasSpace) + // Finish line based on alignment + var glyphsLine = renderGlyphs.AsNativeArray().GetSubArray(startOfLineGlyphIndex, + lastWordStartCharacterGlyphIndex - startOfLineGlyphIndex); + ApplyHorizontalAlignmentToGlyphs(ref glyphsLine, + ref characterGlyphIndicesWithPreceedingSpacesInLine, + baseConfiguration.maxLineWidth, + textConfiguration.m_lineJustification); + + if (lineCount > 0) { - // What pushed us past the line width was a space character. - // The previous character was not a space, and we don't - // want to render this character at the start of the next line. - // We drop this space character instead and allow the next - // character to line-wrap, space or not. - dropSpace = true; - accumulatedSpaces--; + accumulatedVerticalOffset += currentLineHeight + ascentLineDelta; + ApplyVerticalOffsetToGlyphs(ref glyphsLine, accumulatedVerticalOffset); + lastCommittedStartOfLineGlyphIndex = startOfLineGlyphIndex; } + accumulatedVerticalOffset += decentLineDelta; // Todo: Delta should be computed per glyph + //apply user configurable line and paragraph spacing + accumulatedVerticalOffset += + (baseConfiguration.lineSpacing + + (currentRune.value == 10 || currentRune.value == 0x2029 ? baseConfiguration.paragraphSpacing : 0)) * currentEmScale; + + //reset line status + maxLineAscender = float.MinValue; + maxLineDescender = float.MaxValue; + + startOfLineGlyphIndex = lastWordStartCharacterGlyphIndex; + isLineStart = true; + lineCount++; + + xAdvance -= xOffsetChange; - var yOffsetChange = font.lineHeight * currentElementScale; - var xOffsetChange = renderGlyphs[lastWordStartCharacterGlyphIndex].blPosition.x; - if (xOffsetChange > 0 && !dropSpace) // Always allow one visible character + // Adjust the vertices of the previous render glyphs in the word + var glyphPtr = (RenderGlyph*)renderGlyphs.GetUnsafePtr(); + for (int i = lastWordStartCharacterGlyphIndex; i < renderGlyphs.Length; i++) { - // Finish line based on alignment - var glyphsLine = renderGlyphs.AsNativeArray().GetSubArray(startOfLineGlyphIndex, - lastWordStartCharacterGlyphIndex - startOfLineGlyphIndex); - ApplyHorizontalAlignmentToGlyphs(ref glyphsLine, - ref characterGlyphIndicesWithPreceedingSpacesInLine, - baseConfiguration.maxLineWidth, - textConfiguration.m_lineJustification); - startOfLineGlyphIndex = lastWordStartCharacterGlyphIndex; - lineCount++; - - cumulativeOffset.x -= xOffsetChange; - cumulativeOffset.y -= yOffsetChange; - - //Adjust the vertices of the previous render glyphs in the word - var glyphPtr = (RenderGlyph*)renderGlyphs.GetUnsafePtr(); - for (int i = lastWordStartCharacterGlyphIndex; i < renderGlyphs.Length; i++) - { - glyphPtr[i].blPosition.y -= yOffsetChange; - glyphPtr[i].blPosition.x -= xOffsetChange; - glyphPtr[i].trPosition.y -= yOffsetChange; - glyphPtr[i].trPosition.x -= xOffsetChange; - } + glyphPtr[i].blPosition.y -= yOffsetChange; + glyphPtr[i].blPosition.x -= xOffsetChange; + glyphPtr[i].trPosition.y -= yOffsetChange; + glyphPtr[i].trPosition.x -= xOffsetChange; } - } - //Detect start of word - if (currentRune.value == 32 || //Space - currentRune.value == 9 || //Tab - currentRune.value == 45 || //Hyphen Minus - currentRune.value == 173 || //Soft hyphen - currentRune.value == 8203 || //Zero width space - currentRune.value == 8204 || //Zero width non-joiner - currentRune.value == 8205) //Zero width joiner - { - lastWordStartCharacterGlyphIndex = renderGlyphs.Length; - mappingWriter.AddWordStart(renderGlyphs.Length); + // Commit the line to the writer + // Todo: Currently, lines are paragraphs. Once we have soft lines, + // this is where we want the addition to go. I tested this. It works. + // mappingWriter.AddLineStart(lastWordStartCharacterGlyphIndex); } + } - if (currentRune.value == 32) - { - accumulatedSpaces++; - prevWasSpace = true; - } - else if (prevWasSpace) - { - prevWasSpace = false; - } - #endregion + //Detect start of word + if (currentRune.value == 10 || // line feed + currentRune.value == 11 || // vertical tab + currentRune.value == 13 || // carriage return + currentRune.value == 32 || //Space + currentRune.value == 9 || //Tab + currentRune.value == 45 || //Hyphen Minus + currentRune.value == 173 || //Soft hyphen + currentRune.value == 8203 || //Zero width space + currentRune.value == 8204 || //Zero width non-joiner + currentRune.value == 8205) //Zero width joiner + { + lastWordStartCharacterGlyphIndex = renderGlyphs.Length; + } + else if (lastWordStartCharacterGlyphIndex + 1 == renderGlyphs.Length) + { + // We have to delay by one or else we register a word for every space. + mappingWriter.AddWordStart(lastWordStartCharacterGlyphIndex); } + + if (currentRune.value == 32) + accumulatedSpaces++; + #endregion } var finalGlyphsLine = renderGlyphs.AsNativeArray().GetSubArray(startOfLineGlyphIndex, renderGlyphs.Length - startOfLineGlyphIndex); { var overrideMode = textConfiguration.m_lineJustification; - if ((overrideMode) == HorizontalAlignmentOptions.Justified) + if (overrideMode == HorizontalAlignmentOptions.Justified) { // Don't perform justified spacing for the last line. overrideMode = HorizontalAlignmentOptions.Left; } ApplyHorizontalAlignmentToGlyphs(ref finalGlyphsLine, ref characterGlyphIndicesWithPreceedingSpacesInLine, baseConfiguration.maxLineWidth, overrideMode); + if (lineCount > 0) + { + accumulatedVerticalOffset += currentLineHeight; + ApplyVerticalOffsetToGlyphs(ref finalGlyphsLine, accumulatedVerticalOffset); + } } lineCount++; - ApplyVerticalAlignmentToGlyphs(ref renderGlyphs, lineCount, baseConfiguration.verticalAlignment, ref font, baseConfiguration.fontSize); + ApplyVerticalAlignmentToGlyphs(ref renderGlyphs, topAnchor, bottomAnchor, accumulatedVerticalOffset, baseConfiguration.verticalAlignment); + } + + public struct PrevCurNext + { + public CalliString.Enumerator prev; + public CalliString.Enumerator current; + public CalliString.Enumerator next; + + public bool previousIsValid; + public bool currentIsValid; + public bool nextIsValid; + + public PrevCurNext(CalliString.Enumerator enumerator) + { + previousIsValid = false; + prev = default; + currentIsValid = false; + current = default; + nextIsValid = enumerator.MoveNext(); + next = enumerator; + } + + public void MoveNext() + { + prev = current; + current = next; + previousIsValid = currentIsValid; + currentIsValid = nextIsValid; + nextIsValid = next.MoveNext(); + } + } + + static float GetTopAnchorForConfig(ref FontBlob font, VerticalAlignmentOptions verticalMode, float baseScale, float oldValue = float.PositiveInfinity) + { + bool replace = oldValue == float.PositiveInfinity; + switch (verticalMode) + { + case VerticalAlignmentOptions.TopBase: return 0f; + case VerticalAlignmentOptions.MiddleTopAscentToBottomDescent: + case VerticalAlignmentOptions.TopAscent: return baseScale * math.max(font.ascentLine - font.baseLine, math.select(oldValue, float.NegativeInfinity, replace)); + case VerticalAlignmentOptions.TopDescent: return baseScale * math.min(font.descentLine - font.baseLine, oldValue); + case VerticalAlignmentOptions.TopCap: return baseScale * math.max(font.capLine - font.baseLine, math.select(oldValue, float.NegativeInfinity, replace)); + case VerticalAlignmentOptions.TopMean: return baseScale * math.max(font.meanLine - font.baseLine, math.select(oldValue, float.NegativeInfinity, replace)); + default: return 0f; + } + } + + static float GetBottomAnchorForConfig(ref FontBlob font, VerticalAlignmentOptions verticalMode, float baseScale, float oldValue = float.PositiveInfinity) + { + bool replace = oldValue == float.PositiveInfinity; + switch (verticalMode) + { + case VerticalAlignmentOptions.BottomBase: return 0f; + case VerticalAlignmentOptions.BottomAscent: return baseScale * math.max(font.ascentLine - font.baseLine, math.select(oldValue, float.NegativeInfinity, replace)); + case VerticalAlignmentOptions.MiddleTopAscentToBottomDescent: + case VerticalAlignmentOptions.BottomDescent: return baseScale * math.min(font.descentLine - font.baseLine, oldValue); + case VerticalAlignmentOptions.BottomCap: return baseScale * math.max(font.capLine - font.baseLine, math.select(oldValue, float.NegativeInfinity, replace)); + case VerticalAlignmentOptions.BottomMean: return baseScale * math.max(font.meanLine - font.baseLine, math.select(oldValue, float.NegativeInfinity, replace)); + default: return 0f; + } } static unsafe void ApplyHorizontalAlignmentToGlyphs(ref NativeArray glyphs, @@ -462,45 +650,94 @@ static unsafe void ApplyHorizontalAlignmentToGlyphs(ref NativeArray characterGlyphIndicesWithPreceedingSpacesInLine.Clear(); } + static unsafe void ApplyVerticalOffsetToGlyphs(ref NativeArray glyphs, float accumulatedVerticalOffset) + { + for (int i = 0; i < glyphs.Length; i++) + { + var glyph = glyphs[i]; + glyph.blPosition.y -= accumulatedVerticalOffset; + glyph.trPosition.y -= accumulatedVerticalOffset; + glyphs[i] = glyph; + } + } + static unsafe void ApplyVerticalAlignmentToGlyphs(ref DynamicBuffer glyphs, - int fullLineCount, - VerticalAlignmentOptions alignMode, - ref FontBlob font, - float fontSize) + float topAnchor, + float bottomAnchor, + float accumulatedVerticalOffset, + VerticalAlignmentOptions alignMode) { var glyphsPtr = (RenderGlyph*)glyphs.GetUnsafePtr(); - if ((alignMode) == VerticalAlignmentOptions.Top) + switch (alignMode) { - // Positions were calculated relative to the baseline. - // Shift everything down so that y = 0 is on the ascent line. - var offset = font.ascentLine - font.baseLine; - offset *= font.baseScale * fontSize; - for (int i = 0; i < glyphs.Length; i++) + case VerticalAlignmentOptions.TopBase: + return; + case VerticalAlignmentOptions.TopAscent: + case VerticalAlignmentOptions.TopDescent: + case VerticalAlignmentOptions.TopCap: + case VerticalAlignmentOptions.TopMean: { - glyphsPtr[i].blPosition.y -= offset; - glyphsPtr[i].trPosition.y -= offset; + // Positions were calculated relative to the baseline. + // Shift everything down so that y = 0 is on the target line. + for (int i = 0; i < glyphs.Length; i++) + { + glyphsPtr[i].blPosition.y -= topAnchor; + glyphsPtr[i].trPosition.y -= topAnchor; + } + break; } - } - else if ((alignMode) == VerticalAlignmentOptions.Middle) - { - float newlineSpace = (fullLineCount - 1) * font.lineHeight * font.baseScale * fontSize; - float fullHeight = newlineSpace + (font.ascentLine - font.baseLine) * font.baseScale * fontSize; - var offset = fullHeight / 2f - font.ascentLine * font.baseScale * fontSize; - for (int i = 0; i < glyphs.Length; i++) + case VerticalAlignmentOptions.BottomBase: + case VerticalAlignmentOptions.BottomAscent: + case VerticalAlignmentOptions.BottomDescent: + case VerticalAlignmentOptions.BottomCap: + case VerticalAlignmentOptions.BottomMean: { - glyphsPtr[i].blPosition.y += offset; - glyphsPtr[i].trPosition.y += offset; + float offset = accumulatedVerticalOffset - bottomAnchor; + for (int i = 0; i < glyphs.Length; i++) + { + glyphsPtr[i].blPosition.y += offset; + glyphsPtr[i].trPosition.y += offset; + } + break; + } + case VerticalAlignmentOptions.MiddleTopAscentToBottomDescent: + { + float fullHeight = accumulatedVerticalOffset - bottomAnchor + topAnchor; + float offset = fullHeight / 2f; + for (int i = 0; i < glyphs.Length; i++) + { + glyphsPtr[i].blPosition.y += offset; + glyphsPtr[i].trPosition.y += offset; + } + break; } } - else // Bottom + } + + static unsafe void SwapRune(ref Unicode.Rune rune, ref ActiveTextConfiguration textConfiguration, out float smallCapsMultiplier) + { + smallCapsMultiplier = 1f; + + // Todo: Burst does not support language methods, and char only supports the UTF-16 subset + // of characters. We should encode upper and lower cross-references into the font blobs or + // figure out the formulas for all other languages. Right now only ascii is supported. + if ((textConfiguration.m_fontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase) + { + // If this character is lowercase, switch to uppercase. + rune = rune.ToUpper(); + } + else if ((textConfiguration.m_fontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase) { - // Todo: Should we just leave the y = 0 on the baseline instead of the descent line? - float newlineSpace = (fullLineCount - 1) * font.lineHeight * font.baseScale * fontSize; - var offset = newlineSpace + (font.baseLine - font.descentLine) * font.baseScale * fontSize; - for (int i = 0; i < glyphs.Length; i++) + // If this character is uppercase, switch to lowercase. + rune = rune.ToLower(); + } + else if ((textConfiguration.m_fontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps) + { + var oldUnicode = rune; + rune = rune.ToUpper(); + if (rune != oldUnicode) { - glyphsPtr[i].blPosition.y += offset; - glyphsPtr[i].trPosition.y += offset; + smallCapsMultiplier = 0.8f; } } } diff --git a/Calligraphics/Internal/RichText/CalliExtensions.cs b/Calligraphics/Internal/RichText/CalliExtensions.cs index 3dcc022..2590ca3 100644 --- a/Calligraphics/Internal/RichText/CalliExtensions.cs +++ b/Calligraphics/Internal/RichText/CalliExtensions.cs @@ -32,6 +32,41 @@ public static Unicode.Rune ToUpper(this Unicode.Rune rune) return new Unicode.Rune(rune.value - (((uint)(rune.value - 'a') <= ('z' - 'a')) ? 0x20 : 0)); return rune; } + + public static bool IsLatin1(this Unicode.Rune rune) { return rune.value < 0x100; } + + public static bool IsWhiteSpace(this Unicode.Rune rune) + { + // https://en.wikipedia.org/wiki/Whitespace_character#Unicode + var value = rune.value; + if (IsLatin1(rune)) + { + return value == ' ' + || (value >= 0x9 && value <= 0xD) // CHARACTER TABULATION (U+0009), LINE FEED (U+000A), LINE TABULATION (U+000B), FORM FEED (U+000C), CARRIAGE RETURN (U+000D) + || value == 0xA0 // NO-BREAK SPACE + || value == 0x85 // NEXT LINE + ; + } + + return value == 0x1680 // OGHAM SPACE MARK + || (value >= 0x2000 && value <= 0x200A) // EN QUAD(U+2000) + // EM QUAD(U+2001) + // EN SPACE(U+2002) + // EM SPACE(U+2003) + // THREE - PER - EM SPACE(U + 2004) + // FOUR - PER - EM SPACE(U + 2005) + // SIX - PER - EM SPACE(U + 2006) + // FIGURE SPACE(U+2007) + // PUNCTUATION SPACE(U+2008) + // THIN SPACE(U+2009) + // HAIR SPACE(U+200A) + || value == 0x2028 // LINE SEPARATOR + || value == 0x2029 // PARAGRAPH SEPARATOR + || value == 0x202F // NARROW NO-BREAK SPACE + || value == 0x205F // MEDIUM MATHEMATICAL SPACE + || value == 0x3000 // IDEOGRAPHIC SPACE + ; + } } } diff --git a/Calligraphics/Internal/RichText/RichTextParser.cs b/Calligraphics/Internal/RichText/RichTextParser.cs index 063099e..2bd2f2a 100644 --- a/Calligraphics/Internal/RichText/RichTextParser.cs +++ b/Calligraphics/Internal/RichText/RichTextParser.cs @@ -19,9 +19,11 @@ internal static bool ValidateHtmlTag( ref CalliString.Enumerator enumerator, ref FontMaterialSet fontMaterialSet, in TextBaseConfiguration baseConfiguration, - ref TextConfiguration textConfiguration, - ref FixedList512Bytes richTextTagIndentifiers) //this is just a cache to avoid allocation + ref TextConfigurationStack textConfigurationStack, + ref TextGenerationStateCommands textGenerationStateCommands, + int characterCount) // CharacterCount is a temporary argument until we replace the enumerator. { + ref var richTextTagIndentifiers = ref textConfigurationStack.richTextTagIndentifiers; richTextTagIndentifiers.Clear(); int tagCharCount = 0; int tagByteCount = 0; @@ -186,21 +188,21 @@ internal static bool ValidateHtmlTag( } ref var firstTagIndentifier = ref richTextTagIndentifiers.ElementAt(0); - calliString.GetSubString(ref textConfiguration.m_htmlTag, startByteIndex, tagByteCount); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, startByteIndex, tagByteCount); //#region Rich Text Tag Processing //#if !RICH_TEXT_ENABLED // Special handling of the no parsing tag tag - if (textConfiguration.tag_NoParsing && (firstTagIndentifier.nameHashCode != 53822163 && firstTagIndentifier.nameHashCode != 49429939)) + if (textConfigurationStack.m_tagNoParsing && (firstTagIndentifier.nameHashCode != 53822163 && firstTagIndentifier.nameHashCode != 49429939)) return false; else if (firstTagIndentifier.nameHashCode == 53822163 || firstTagIndentifier.nameHashCode == 49429939) { - textConfiguration.tag_NoParsing = false; + textConfigurationStack.m_tagNoParsing = false; return true; } // Color tag just starting with hex (no assignment) - if (textConfiguration.m_htmlTag[0] == 35) + if (textConfigurationStack.m_htmlTag[0] == 35) { // tagCharCount == 4: Color <#FFF> 3 Hex values (short form) // tagCharCount == 5: Color <#FFF7> 4 Hex values with alpha (short form) @@ -208,8 +210,8 @@ internal static bool ValidateHtmlTag( // tagCharCount == 9: Color <#FF00FF00> with alpha if (tagCharCount == 4 || tagCharCount == 5 || tagCharCount == 7 || tagCharCount == 9) { - textConfiguration.m_htmlColor = HexCharsToColor(textConfiguration.m_htmlTag, tagCharCount); - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = HexCharsToColor(textConfigurationStack.m_htmlTag, tagCharCount); + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; } } @@ -218,129 +220,132 @@ internal static bool ValidateHtmlTag( float value = 0; float fontScale; - ref var currentFont = ref fontMaterialSet[textConfiguration.m_currentFontMaterialIndex]; + ref var currentFont = ref fontMaterialSet[textConfigurationStack.m_currentFontMaterialIndex]; switch (firstTagIndentifier.nameHashCode) { case 98: // case 66: // - textConfiguration.m_fontStyleInternal |= FontStyles.Bold; - textConfiguration.m_fontStyleStack.Add(FontStyles.Bold); + textConfigurationStack.m_fontStyleInternal |= FontStyles.Bold; + textConfigurationStack.m_fontStyleStack.Add(FontStyles.Bold); - textConfiguration.m_fontWeightInternal = FontWeight.Bold; + textConfigurationStack.m_fontWeightInternal = FontWeight.Bold; return true; case 427: // case 395: // if ((baseConfiguration.fontStyle & FontStyles.Bold) != FontStyles.Bold) { - if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Bold) == 0) + if (textConfigurationStack.m_fontStyleStack.Remove(FontStyles.Bold) == 0) { - textConfiguration.m_fontStyleInternal &= ~FontStyles.Bold; - textConfiguration.m_fontWeightInternal = textConfiguration.m_fontWeightStack.Peek(); + textConfigurationStack.m_fontStyleInternal &= ~FontStyles.Bold; + textConfigurationStack.m_fontWeightInternal = textConfigurationStack.m_fontWeightStack.Peek(); } } return true; case 105: // case 73: // - textConfiguration.m_fontStyleInternal |= FontStyles.Italic; - textConfiguration.m_fontStyleStack.Add(FontStyles.Italic); + textConfigurationStack.m_fontStyleInternal |= FontStyles.Italic; + textConfigurationStack.m_fontStyleStack.Add(FontStyles.Italic); if (richTextTagIndentifiers.Length > 1 && (richTextTagIndentifiers[1].nameHashCode == 276531 || richTextTagIndentifiers[1].nameHashCode == 186899)) { // Reject tag if value is invalid. - calliString.GetSubString(ref textConfiguration.m_htmlTag, richTextTagIndentifiers[1].valueStartIndex, richTextTagIndentifiers[1].valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, richTextTagIndentifiers[1].valueStartIndex, richTextTagIndentifiers[1].valueLength); - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; - textConfiguration.m_italicAngle = (short)value; + textConfigurationStack.m_italicAngle = (short)value; // Make sure angle is within valid range. - if (textConfiguration.m_italicAngle < -180 || textConfiguration.m_italicAngle > 180) + if (textConfigurationStack.m_italicAngle < -180 || textConfigurationStack.m_italicAngle > 180) return false; } else - textConfiguration.m_italicAngle = currentFont.italicsStyleSlant; + textConfigurationStack.m_italicAngle = currentFont.italicsStyleSlant; - textConfiguration.m_italicAngleStack.Add(textConfiguration.m_italicAngle); + textConfigurationStack.m_italicAngleStack.Add(textConfigurationStack.m_italicAngle); return true; case 434: // case 402: // if ((baseConfiguration.fontStyle & FontStyles.Italic) != FontStyles.Italic) { - textConfiguration.m_italicAngle = textConfiguration.m_italicAngleStack.RemoveExceptRoot(); + textConfigurationStack.m_italicAngle = textConfigurationStack.m_italicAngleStack.RemoveExceptRoot(); - if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Italic) == 0) - textConfiguration.m_fontStyleInternal &= ~FontStyles.Italic; + if (textConfigurationStack.m_fontStyleStack.Remove(FontStyles.Italic) == 0) + textConfigurationStack.m_fontStyleInternal &= ~FontStyles.Italic; } return true; case 115: // case 83: // - textConfiguration.m_fontStyleInternal |= FontStyles.Strikethrough; - textConfiguration.m_fontStyleStack.Add(FontStyles.Strikethrough); + textConfigurationStack.m_fontStyleInternal |= FontStyles.Strikethrough; + textConfigurationStack.m_fontStyleStack.Add(FontStyles.Strikethrough); if (richTextTagIndentifiers.Length > 1 && (richTextTagIndentifiers[1].nameHashCode == 281955 || richTextTagIndentifiers[1].nameHashCode == 192323)) { - calliString.GetSubString(ref textConfiguration.m_htmlTag, richTextTagIndentifiers[1].valueStartIndex, richTextTagIndentifiers[1].valueLength); - charCount = richTextTagIndentifiers[1].valueLength - richTextTagIndentifiers[1].valueStartIndex; - textConfiguration.m_strikethroughColor = HexCharsToColor(textConfiguration.m_htmlTag, charCount); - textConfiguration.m_strikethroughColor.a = textConfiguration.m_htmlColor.a < - textConfiguration.m_strikethroughColor.a ? (byte)(textConfiguration.m_htmlColor.a) : (byte)(textConfiguration - . - m_strikethroughColor - .a); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, richTextTagIndentifiers[1].valueStartIndex, richTextTagIndentifiers[1].valueLength); + charCount = richTextTagIndentifiers[1].valueLength - richTextTagIndentifiers[1].valueStartIndex; + textConfigurationStack.m_strikethroughColor = HexCharsToColor(textConfigurationStack.m_htmlTag, charCount); + textConfigurationStack.m_strikethroughColor.a = textConfigurationStack.m_htmlColor.a < + textConfigurationStack.m_strikethroughColor.a ? (byte)(textConfigurationStack.m_htmlColor.a) : (byte)( + textConfigurationStack + . + m_strikethroughColor + .a); } else - textConfiguration.m_strikethroughColor = textConfiguration.m_htmlColor; + textConfigurationStack.m_strikethroughColor = textConfigurationStack.m_htmlColor; - textConfiguration.m_strikethroughColorStack.Add(textConfiguration.m_strikethroughColor); + textConfigurationStack.m_strikethroughColorStack.Add(textConfigurationStack.m_strikethroughColor); return true; case 444: // case 412: // if ((baseConfiguration.fontStyle & FontStyles.Strikethrough) != FontStyles.Strikethrough) { - if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Strikethrough) == 0) - textConfiguration.m_fontStyleInternal &= ~FontStyles.Strikethrough; + if (textConfigurationStack.m_fontStyleStack.Remove(FontStyles.Strikethrough) == 0) + textConfigurationStack.m_fontStyleInternal &= ~FontStyles.Strikethrough; } - textConfiguration.m_strikethroughColor = textConfiguration.m_strikethroughColorStack.RemoveExceptRoot(); + textConfigurationStack.m_strikethroughColor = textConfigurationStack.m_strikethroughColorStack.RemoveExceptRoot(); return true; case 117: // case 85: // - textConfiguration.m_fontStyleInternal |= FontStyles.Underline; - textConfiguration.m_fontStyleStack.Add(FontStyles.Underline); + textConfigurationStack.m_fontStyleInternal |= FontStyles.Underline; + textConfigurationStack.m_fontStyleStack.Add(FontStyles.Underline); if (richTextTagIndentifiers.Length > 1 && (richTextTagIndentifiers[1].nameHashCode == 281955 || richTextTagIndentifiers[1].nameHashCode == 192323)) { - calliString.GetSubString(ref textConfiguration.m_htmlTag, richTextTagIndentifiers[1].valueStartIndex, richTextTagIndentifiers[1].valueLength); - charCount = richTextTagIndentifiers[1].valueLength - richTextTagIndentifiers[1].valueStartIndex; - textConfiguration.m_underlineColor = HexCharsToColor(textConfiguration.m_htmlTag, charCount); - textConfiguration.m_underlineColor.a = textConfiguration.m_htmlColor.a < - textConfiguration.m_underlineColor.a ? (byte)(textConfiguration.m_htmlColor.a) : (byte)(textConfiguration. - m_underlineColor.a); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, richTextTagIndentifiers[1].valueStartIndex, richTextTagIndentifiers[1].valueLength); + charCount = richTextTagIndentifiers[1].valueLength - richTextTagIndentifiers[1].valueStartIndex; + textConfigurationStack.m_underlineColor = HexCharsToColor(textConfigurationStack.m_htmlTag, charCount); + textConfigurationStack.m_underlineColor.a = textConfigurationStack.m_htmlColor.a < + textConfigurationStack.m_underlineColor.a ? (byte)(textConfigurationStack.m_htmlColor.a) : (byte)( + textConfigurationStack. + m_underlineColor + .a); } else - textConfiguration.m_underlineColor = textConfiguration.m_htmlColor; + textConfigurationStack.m_underlineColor = textConfigurationStack.m_htmlColor; - textConfiguration.m_underlineColorStack.Add(textConfiguration.m_underlineColor); + textConfigurationStack.m_underlineColorStack.Add(textConfigurationStack.m_underlineColor); return true; case 446: // case 414: // if ((baseConfiguration.fontStyle & FontStyles.Underline) != FontStyles.Underline) { - textConfiguration.m_underlineColor = textConfiguration.m_underlineColorStack.RemoveExceptRoot(); + textConfigurationStack.m_underlineColor = textConfigurationStack.m_underlineColorStack.RemoveExceptRoot(); - if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Underline) == 0) - textConfiguration.m_fontStyleInternal &= ~FontStyles.Underline; + if (textConfigurationStack.m_fontStyleStack.Remove(FontStyles.Underline) == 0) + textConfigurationStack.m_fontStyleInternal &= ~FontStyles.Underline; } - textConfiguration.m_underlineColor = textConfiguration.m_underlineColorStack.RemoveExceptRoot(); + textConfigurationStack.m_underlineColor = textConfigurationStack.m_underlineColorStack.RemoveExceptRoot(); return true; case 43045: // case 30245: // - textConfiguration.m_fontStyleInternal |= FontStyles.Highlight; - textConfiguration.m_fontStyleStack.Add(FontStyles.Highlight); + textConfigurationStack.m_fontStyleInternal |= FontStyles.Highlight; + textConfigurationStack.m_fontStyleStack.Add(FontStyles.Highlight); Color32 highlightColor = new Color32(255, 255, 0, 64); RectOffsets highlightPadding = RectOffsets.zero; @@ -358,17 +363,19 @@ internal static bool ValidateHtmlTag( if (richTextTagIndentifiers[i].valueType == TagValueType.ColorValue) { //is this a bug in TMP Pro? -->should be richTextTagIndentifiers[i] and not firstTagIndentifier - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); charCount = firstTagIndentifier.valueLength - firstTagIndentifier.valueStartIndex; - highlightColor = HexCharsToColor(textConfiguration.m_htmlTag, charCount); + highlightColor = HexCharsToColor(textConfigurationStack.m_htmlTag, charCount); } break; // Color tagIndentifier case 281955: - calliString.GetSubString(ref textConfiguration.m_htmlTag, richTextTagIndentifiers[i].valueStartIndex, richTextTagIndentifiers[i].valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, + richTextTagIndentifiers[i].valueStartIndex, + richTextTagIndentifiers[i].valueLength); charCount = richTextTagIndentifiers[i].valueLength - richTextTagIndentifiers[i].valueStartIndex; - highlightColor = HexCharsToColor(textConfiguration.m_htmlTag, charCount); + highlightColor = HexCharsToColor(textConfigurationStack.m_htmlTag, charCount); break; // Padding tagIndentifier @@ -382,140 +389,144 @@ internal static bool ValidateHtmlTag( } } - highlightColor.a = textConfiguration.m_htmlColor.a < highlightColor.a ? (byte)(textConfiguration.m_htmlColor.a) : (byte)(highlightColor.a); + highlightColor.a = textConfigurationStack.m_htmlColor.a < highlightColor.a ? (byte)(textConfigurationStack.m_htmlColor.a) : (byte)(highlightColor.a); HighlightState state = new HighlightState(highlightColor, highlightPadding); - textConfiguration.m_highlightStateStack.Add(state); + textConfigurationStack.m_highlightStateStack.Add(state); return true; case 155892: // case 143092: // if ((baseConfiguration.fontStyle & FontStyles.Highlight) != FontStyles.Highlight) { - textConfiguration.m_highlightStateStack.RemoveExceptRoot(); + textConfigurationStack.m_highlightStateStack.RemoveExceptRoot(); - if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Highlight) == 0) - textConfiguration.m_fontStyleInternal &= ~FontStyles.Highlight; + if (textConfigurationStack.m_fontStyleStack.Remove(FontStyles.Highlight) == 0) + textConfigurationStack.m_fontStyleInternal &= ~FontStyles.Highlight; } return true; case 6552: // case 4728: // - textConfiguration.m_fontScaleMultiplier *= currentFont.subscriptSize > 0 ? currentFont.subscriptSize : 1; - textConfiguration.m_baselineOffsetStack.Add(textConfiguration.m_baselineOffset); + textConfigurationStack.m_fontScaleMultiplier *= currentFont.subscriptSize > 0 ? currentFont.subscriptSize : 1; + textConfigurationStack.m_baselineOffsetStack.Add(textConfigurationStack.m_baselineOffset); fontScale = - (textConfiguration.m_currentFontSize / currentFont.pointSize * currentFont.scale * (baseConfiguration.isOrthographic ? 1 : 0.1f)); - textConfiguration.m_baselineOffset += currentFont.subscriptOffset * fontScale * textConfiguration.m_fontScaleMultiplier; + (textConfigurationStack.m_currentFontSize / currentFont.pointSize * currentFont.scale * (baseConfiguration.isOrthographic ? 1 : 0.1f)); + textConfigurationStack.m_baselineOffset += currentFont.subscriptOffset * fontScale * textConfigurationStack.m_fontScaleMultiplier; - textConfiguration.m_fontStyleStack.Add(FontStyles.Subscript); - textConfiguration.m_fontStyleInternal |= FontStyles.Subscript; + textConfigurationStack.m_fontStyleStack.Add(FontStyles.Subscript); + textConfigurationStack.m_fontStyleInternal |= FontStyles.Subscript; return true; case 22673: // case 20849: // - if ((textConfiguration.m_fontStyleInternal & FontStyles.Subscript) == FontStyles.Subscript) + if ((textConfigurationStack.m_fontStyleInternal & FontStyles.Subscript) == FontStyles.Subscript) { - if (textConfiguration.m_fontScaleMultiplier < 1) + if (textConfigurationStack.m_fontScaleMultiplier < 1) { - textConfiguration.m_baselineOffset = textConfiguration.m_baselineOffsetStack.RemoveExceptRoot(); - textConfiguration.m_fontScaleMultiplier /= currentFont.subscriptSize > 0 ? currentFont.subscriptSize : 1; + textConfigurationStack.m_baselineOffset = textConfigurationStack.m_baselineOffsetStack.RemoveExceptRoot(); + textConfigurationStack.m_fontScaleMultiplier /= currentFont.subscriptSize > 0 ? currentFont.subscriptSize : 1; } - if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Subscript) == 0) - textConfiguration.m_fontStyleInternal &= ~FontStyles.Subscript; + if (textConfigurationStack.m_fontStyleStack.Remove(FontStyles.Subscript) == 0) + textConfigurationStack.m_fontStyleInternal &= ~FontStyles.Subscript; } return true; case 6566: // case 4742: // - textConfiguration.m_fontScaleMultiplier *= currentFont.superscriptSize > 0 ? currentFont.superscriptSize : 1; - textConfiguration.m_baselineOffsetStack.Add(textConfiguration.m_baselineOffset); + textConfigurationStack.m_fontScaleMultiplier *= currentFont.superscriptSize > 0 ? currentFont.superscriptSize : 1; + textConfigurationStack.m_baselineOffsetStack.Add(textConfigurationStack.m_baselineOffset); fontScale = - (textConfiguration.m_currentFontSize / currentFont.pointSize * currentFont.scale * (baseConfiguration.isOrthographic ? 1 : 0.1f)); - textConfiguration.m_baselineOffset += currentFont.superscriptOffset * fontScale * textConfiguration.m_fontScaleMultiplier; + (textConfigurationStack.m_currentFontSize / currentFont.pointSize * currentFont.scale * (baseConfiguration.isOrthographic ? 1 : 0.1f)); + textConfigurationStack.m_baselineOffset += currentFont.superscriptOffset * fontScale * textConfigurationStack.m_fontScaleMultiplier; - textConfiguration.m_fontStyleStack.Add(FontStyles.Superscript); - textConfiguration.m_fontStyleInternal |= FontStyles.Superscript; + textConfigurationStack.m_fontStyleStack.Add(FontStyles.Superscript); + textConfigurationStack.m_fontStyleInternal |= FontStyles.Superscript; return true; case 22687: // case 20863: // - if ((textConfiguration.m_fontStyleInternal & FontStyles.Superscript) == FontStyles.Superscript) + if ((textConfigurationStack.m_fontStyleInternal & FontStyles.Superscript) == FontStyles.Superscript) { - if (textConfiguration.m_fontScaleMultiplier < 1) + if (textConfigurationStack.m_fontScaleMultiplier < 1) { - textConfiguration.m_baselineOffset = textConfiguration.m_baselineOffsetStack.RemoveExceptRoot(); - textConfiguration.m_fontScaleMultiplier /= currentFont.superscriptSize > 0 ? currentFont.superscriptSize : 1; + textConfigurationStack.m_baselineOffset = textConfigurationStack.m_baselineOffsetStack.RemoveExceptRoot(); + textConfigurationStack.m_fontScaleMultiplier /= currentFont.superscriptSize > 0 ? currentFont.superscriptSize : 1; } - if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Superscript) == 0) - textConfiguration.m_fontStyleInternal &= ~FontStyles.Superscript; + if (textConfigurationStack.m_fontStyleStack.Remove(FontStyles.Superscript) == 0) + textConfigurationStack.m_fontStyleInternal &= ~FontStyles.Superscript; } return true; case -330774850: // case 2012149182: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch ((int)value) { case 100: - textConfiguration.m_fontWeightInternal = FontWeight.Thin; + textConfigurationStack.m_fontWeightInternal = FontWeight.Thin; break; case 200: - textConfiguration.m_fontWeightInternal = FontWeight.ExtraLight; + textConfigurationStack.m_fontWeightInternal = FontWeight.ExtraLight; break; case 300: - textConfiguration.m_fontWeightInternal = FontWeight.Light; + textConfigurationStack.m_fontWeightInternal = FontWeight.Light; break; case 400: - textConfiguration.m_fontWeightInternal = FontWeight.Regular; + textConfigurationStack.m_fontWeightInternal = FontWeight.Regular; break; case 500: - textConfiguration.m_fontWeightInternal = FontWeight.Medium; + textConfigurationStack.m_fontWeightInternal = FontWeight.Medium; break; case 600: - textConfiguration.m_fontWeightInternal = FontWeight.SemiBold; + textConfigurationStack.m_fontWeightInternal = FontWeight.SemiBold; break; case 700: - textConfiguration.m_fontWeightInternal = FontWeight.Bold; + textConfigurationStack.m_fontWeightInternal = FontWeight.Bold; break; case 800: - textConfiguration.m_fontWeightInternal = FontWeight.Heavy; + textConfigurationStack.m_fontWeightInternal = FontWeight.Heavy; break; case 900: - textConfiguration.m_fontWeightInternal = FontWeight.Black; + textConfigurationStack.m_fontWeightInternal = FontWeight.Black; break; } - textConfiguration.m_fontWeightStack.Add(textConfiguration.m_fontWeightInternal); + textConfigurationStack.m_fontWeightStack.Add(textConfigurationStack.m_fontWeightInternal); return true; case -1885698441: // case 457225591: // - textConfiguration.m_fontWeightStack.RemoveExceptRoot(); + textConfigurationStack.m_fontWeightStack.RemoveExceptRoot(); - if (textConfiguration.m_fontStyleInternal == FontStyles.Bold) - textConfiguration.m_fontWeightInternal = FontWeight.Bold; + if (textConfigurationStack.m_fontStyleInternal == FontStyles.Bold) + textConfigurationStack.m_fontWeightInternal = FontWeight.Bold; else - textConfiguration.m_fontWeightInternal = textConfiguration.m_fontWeightStack.Peek(); + textConfigurationStack.m_fontWeightInternal = textConfigurationStack.m_fontWeightStack.Peek(); return true; case 6380: // case 4556: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) { case TagUnitType.Pixels: - textConfiguration.m_xAdvance = value * (baseConfiguration.isOrthographic ? 1.0f : 0.1f); + textGenerationStateCommands.xAdvanceChange = value * (baseConfiguration.isOrthographic ? 1.0f : 0.1f); + textGenerationStateCommands.xAdvanceIsOverwrite = true; return true; case TagUnitType.FontUnits: - textConfiguration.m_xAdvance = value * textConfiguration.m_currentFontSize * (baseConfiguration.isOrthographic ? 1.0f : 0.1f); + textGenerationStateCommands.xAdvanceChange = value * textConfigurationStack.m_currentFontSize * + (baseConfiguration.isOrthographic ? 1.0f : 0.1f); + textGenerationStateCommands.xAdvanceIsOverwrite = true; return true; case TagUnitType.Percentage: - textConfiguration.m_xAdvance = textConfiguration.m_marginWidth * value / 100; + textGenerationStateCommands.xAdvanceChange = textConfigurationStack.m_marginWidth * value / 100; + textGenerationStateCommands.xAdvanceIsOverwrite = true; return true; } return false; @@ -525,17 +536,17 @@ internal static bool ValidateHtmlTag( case 16034505: // case 11642281: // // Reject tag if value is invalid. - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) { case TagUnitType.Pixels: - textConfiguration.m_baselineOffset = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textConfigurationStack.m_baselineOffset = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); return true; case TagUnitType.FontUnits: - textConfiguration.m_baselineOffset = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textConfigurationStack.m_baselineOffset = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfigurationStack.m_currentFontSize; return true; case TagUnitType.Percentage: //m_baselineOffset = m_marginHeight * val / 100; @@ -544,7 +555,7 @@ internal static bool ValidateHtmlTag( return false; case 54741026: // case 50348802: // - textConfiguration.m_baselineOffset = 0; + textConfigurationStack.m_baselineOffset = 0; return true; //case 43991: // //case 31191: // @@ -564,17 +575,17 @@ internal static bool ValidateHtmlTag( //// return true; case 43969: // case 31169: // - textConfiguration.m_isNonBreakingSpace = true; + textConfigurationStack.m_isNonBreakingSpace = true; return true; case 156816: // case 144016: // - textConfiguration.m_isNonBreakingSpace = false; + textConfigurationStack.m_isNonBreakingSpace = false; return true; case 45545: // case 32745: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) @@ -582,64 +593,65 @@ internal static bool ValidateHtmlTag( case TagUnitType.Pixels: if (calliString[5] == 43) // { - textConfiguration.m_currentFontSize = baseConfiguration.fontSize + value; - textConfiguration.m_sizeStack.Add(textConfiguration.m_currentFontSize); + textConfigurationStack.m_currentFontSize = baseConfiguration.fontSize + value; + textConfigurationStack.m_sizeStack.Add(textConfigurationStack.m_currentFontSize); return true; } else if (calliString[5] == 45) // { - textConfiguration.m_currentFontSize = baseConfiguration.fontSize + value; - textConfiguration.m_sizeStack.Add(textConfiguration.m_currentFontSize); + textConfigurationStack.m_currentFontSize = baseConfiguration.fontSize + value; + textConfigurationStack.m_sizeStack.Add(textConfigurationStack.m_currentFontSize); return true; } else // { - textConfiguration.m_currentFontSize = value; - textConfiguration.m_sizeStack.Add(textConfiguration.m_currentFontSize); + textConfigurationStack.m_currentFontSize = value; + textConfigurationStack.m_sizeStack.Add(textConfigurationStack.m_currentFontSize); return true; } case TagUnitType.FontUnits: - textConfiguration.m_currentFontSize = baseConfiguration.fontSize * value; - textConfiguration.m_sizeStack.Add(textConfiguration.m_currentFontSize); + textConfigurationStack.m_currentFontSize = baseConfiguration.fontSize * value; + textConfigurationStack.m_sizeStack.Add(textConfigurationStack.m_currentFontSize); return true; case TagUnitType.Percentage: - textConfiguration.m_currentFontSize = baseConfiguration.fontSize * value / 100; - textConfiguration.m_sizeStack.Add(textConfiguration.m_currentFontSize); + textConfigurationStack.m_currentFontSize = baseConfiguration.fontSize * value / 100; + textConfigurationStack.m_sizeStack.Add(textConfigurationStack.m_currentFontSize); return true; } return false; case 158392: // case 145592: // - textConfiguration.m_currentFontSize = textConfiguration.m_sizeStack.RemoveExceptRoot(); + textConfigurationStack.m_currentFontSize = textConfigurationStack.m_sizeStack.RemoveExceptRoot(); return true; case 41311: // case 28511: // - int fontHashCode = firstTagIndentifier.valueHashCode; + int fontHashCode = firstTagIndentifier.valueHashCode; // Special handling for or if (fontHashCode == 764638571 || fontHashCode == 523367755) { - textConfiguration.m_currentFontMaterialIndex = 0; - textConfiguration.m_fontMaterialIndexStack.Add(0); - return true; } + textConfigurationStack.m_currentFontMaterialIndex = 0; + textConfigurationStack.m_fontMaterialIndexStack.Add(0); + return true; + } - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); for (int i = 0; i < fontMaterialSet.length; i++) { ref var candidateFont = ref fontMaterialSet[i]; - if (textConfiguration.m_htmlTag.Equals(candidateFont.name)) + if (textConfigurationStack.m_htmlTag.Equals(candidateFont.name)) { - textConfiguration.m_currentFontMaterialIndex = i; - textConfiguration.m_fontMaterialIndexStack.Add(i); + textConfigurationStack.m_currentFontMaterialIndex = i; + textConfigurationStack.m_fontMaterialIndexStack.Add(i); return true; } } return false; case 154158: // case 141358: // - { - textConfiguration.m_currentFontMaterialIndex = textConfiguration.m_fontMaterialIndexStack.RemoveExceptRoot(); + { + textConfigurationStack.m_currentFontMaterialIndex = textConfigurationStack.m_fontMaterialIndexStack.RemoveExceptRoot(); return true; } //case 103415287: // @@ -708,18 +720,18 @@ internal static bool ValidateHtmlTag( // } case 320078: // case 230446: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) { case TagUnitType.Pixels: - textConfiguration.m_xAdvance += value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textGenerationStateCommands.xAdvanceChange += value * (baseConfiguration.isOrthographic ? 1 : 0.1f); return true; case TagUnitType.FontUnits: - textConfiguration.m_xAdvance += value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textGenerationStateCommands.xAdvanceChange += value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfigurationStack.m_currentFontSize; return true; case TagUnitType.Percentage: // Not applicable @@ -731,8 +743,9 @@ internal static bool ValidateHtmlTag( if (firstTagIndentifier.valueLength != 3) return false; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); - textConfiguration.m_htmlColor.a = (byte)(HexToInt((char)textConfiguration.m_htmlTag[1]) * 16 + HexToInt((char)textConfiguration.m_htmlTag[2])); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + textConfigurationStack.m_htmlColor.a = + (byte)(HexToInt((char)textConfigurationStack.m_htmlTag[1]) * 16 + HexToInt((char)textConfigurationStack.m_htmlTag[2])); return true; //case 1750458: // @@ -801,58 +814,58 @@ internal static bool ValidateHtmlTag( // return true; case 327550: // case 237918: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) { case TagUnitType.Pixels: - textConfiguration.m_width = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textConfigurationStack.m_width = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: return false; //break; case TagUnitType.Percentage: - textConfiguration.m_width = textConfiguration.m_marginWidth * value / 100; + textConfigurationStack.m_width = textConfigurationStack.m_marginWidth * value / 100; break; } return true; case 1117479: // case 1027847: // - textConfiguration.m_width = -1; + textConfigurationStack.m_width = -1; return true; case 281955: // or case 192323: // // 3 Hex (short hand) - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); tagCharCount -= 6; //remove "color=" from char count - if (textConfiguration.m_htmlTag[0] == 35 && tagCharCount == 4) + if (textConfigurationStack.m_htmlTag[0] == 35 && tagCharCount == 4) { - textConfiguration.m_htmlColor = HexCharsToColor(textConfiguration.m_htmlTag, tagCharCount); - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = HexCharsToColor(textConfigurationStack.m_htmlTag, tagCharCount); + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; } // 4 Hex (short hand) - else if (textConfiguration.m_htmlTag[0] == 35 && tagCharCount == 5) + else if (textConfigurationStack.m_htmlTag[0] == 35 && tagCharCount == 5) { - textConfiguration.m_htmlColor = HexCharsToColor(textConfiguration.m_htmlTag, tagCharCount); - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = HexCharsToColor(textConfigurationStack.m_htmlTag, tagCharCount); + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; } // 3 Hex pairs - if (textConfiguration.m_htmlTag[0] == 35 && tagCharCount == 7) + if (textConfigurationStack.m_htmlTag[0] == 35 && tagCharCount == 7) { - textConfiguration.m_htmlColor = HexCharsToColor(textConfiguration.m_htmlTag, tagCharCount); - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = HexCharsToColor(textConfigurationStack.m_htmlTag, tagCharCount); + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; } // 4 Hex pairs - else if (textConfiguration.m_htmlTag[0] == 35 && tagCharCount == 9) + else if (textConfigurationStack.m_htmlTag[0] == 35 && tagCharCount == 9) { - textConfiguration.m_htmlColor = HexCharsToColor(textConfiguration.m_htmlTag, tagCharCount); - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = HexCharsToColor(textConfigurationStack.m_htmlTag, tagCharCount); + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; } @@ -860,44 +873,44 @@ internal static bool ValidateHtmlTag( switch (firstTagIndentifier.valueHashCode) { case 125395: // - textConfiguration.m_htmlColor = Color.red; - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = Color.red; + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; case -992792864: // - textConfiguration.m_htmlColor = new Color32(173, 216, 230, 255); - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = new Color32(173, 216, 230, 255); + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; case 3573310: // - textConfiguration.m_htmlColor = Color.blue; - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = Color.blue; + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; case 3680713: // - textConfiguration.m_htmlColor = new Color32(128, 128, 128, 255); - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = new Color32(128, 128, 128, 255); + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; case 117905991: // - textConfiguration.m_htmlColor = Color.black; - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = Color.black; + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; case 121463835: // - textConfiguration.m_htmlColor = Color.green; - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = Color.green; + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; case 140357351: // - textConfiguration.m_htmlColor = Color.white; - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = Color.white; + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; case 26556144: // - textConfiguration.m_htmlColor = new Color32(255, 128, 0, 255); - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = new Color32(255, 128, 0, 255); + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; case -36881330: // - textConfiguration.m_htmlColor = new Color32(160, 32, 240, 255); - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = new Color32(160, 32, 240, 255); + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; case 554054276: // - textConfiguration.m_htmlColor = Color.yellow; - textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); + textConfigurationStack.m_htmlColor = Color.yellow; + textConfigurationStack.m_colorStack.Add(textConfigurationStack.m_htmlColor); return true; } return false; @@ -960,18 +973,18 @@ internal static bool ValidateHtmlTag( case 1983971: // case 1356515: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) { case TagUnitType.Pixels: - textConfiguration.m_cSpacing = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textConfigurationStack.m_cSpacing = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: - textConfiguration.m_cSpacing = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textConfigurationStack.m_cSpacing = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfigurationStack.m_currentFontSize; break; case TagUnitType.Percentage: return false; @@ -979,31 +992,31 @@ internal static bool ValidateHtmlTag( return true; case 7513474: // case 6886018: // - if (!textConfiguration.m_isParsingText) + if (!textConfigurationStack.m_isParsingText) return true; // Adjust xAdvance to remove extra space from last character. - if (textConfiguration.m_characterCount > 0) + if (characterCount > 0) { - textConfiguration.m_xAdvance -= textConfiguration.m_cSpacing; + textGenerationStateCommands.xAdvanceChange -= textConfigurationStack.m_cSpacing; //m_textInfo.characterInfo[m_characterCount - 1].xAdvance = m_xAdvance; } - textConfiguration.m_cSpacing = 0; + textConfigurationStack.m_cSpacing = 0; return true; case 2152041: // case 1524585: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) { case TagUnitType.Pixels: - textConfiguration.m_monoSpacing = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textConfigurationStack.m_monoSpacing = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: - textConfiguration.m_monoSpacing = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textConfigurationStack.m_monoSpacing = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfigurationStack.m_currentFontSize; break; case TagUnitType.Percentage: return false; @@ -1011,68 +1024,69 @@ internal static bool ValidateHtmlTag( return true; case 7681544: // case 7054088: // - textConfiguration.m_monoSpacing = 0; + textConfigurationStack.m_monoSpacing = 0; return true; case 280416: // return false; case 1071884: // case 982252: // - textConfiguration.m_htmlColor = textConfiguration.m_colorStack.RemoveExceptRoot(); + textConfigurationStack.m_htmlColor = textConfigurationStack.m_colorStack.RemoveExceptRoot(); return true; case 2068980: // case 1441524: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) { case TagUnitType.Pixels: - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) - textConfiguration.tag_Indent = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) + textConfigurationStack.m_tagIndent = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: - textConfiguration.tag_Indent = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textConfigurationStack.m_tagIndent = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfigurationStack.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.tag_Indent = textConfiguration.m_marginWidth * value / 100; + textConfigurationStack.m_tagIndent = textConfigurationStack.m_marginWidth * value / 100; break; } - textConfiguration.m_indentStack.Add(textConfiguration.tag_Indent); + textConfigurationStack.m_indentStack.Add(textConfigurationStack.m_tagIndent); - textConfiguration.m_xAdvance = textConfiguration.tag_Indent; + textGenerationStateCommands.xAdvanceChange = textConfigurationStack.m_tagIndent; + textGenerationStateCommands.xAdvanceIsOverwrite = true; return true; case 7598483: // case 6971027: // - textConfiguration.tag_Indent = textConfiguration.m_indentStack.RemoveExceptRoot(); + textConfigurationStack.m_tagIndent = textConfigurationStack.m_indentStack.RemoveExceptRoot(); //m_xAdvance = tag_Indent; return true; case 1109386397: // case -842656867: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) { case TagUnitType.Pixels: - textConfiguration.tag_LineIndent = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textConfigurationStack.m_tagLineIndent = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: - textConfiguration.tag_LineIndent = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textConfigurationStack.m_tagLineIndent = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfigurationStack.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.tag_LineIndent = textConfiguration.m_marginWidth * value / 100; + textConfigurationStack.m_tagLineIndent = textConfigurationStack.m_marginWidth * value / 100; break; } - textConfiguration.m_xAdvance += textConfiguration.tag_LineIndent; + textGenerationStateCommands.xAdvanceChange += textConfigurationStack.m_tagLineIndent; return true; case -445537194: // case 1897386838: // - textConfiguration.tag_LineIndent = 0; + textConfigurationStack.m_tagLineIndent = 0; return true; //case 2246877: // //case 1619421: // @@ -1225,23 +1239,23 @@ internal static bool ValidateHtmlTag( // return true; case 730022849: // case 514803617: // - textConfiguration.m_fontStyleInternal |= FontStyles.LowerCase; - textConfiguration.m_fontStyleStack.Add(FontStyles.LowerCase); + textConfigurationStack.m_fontStyleInternal |= FontStyles.LowerCase; + textConfigurationStack.m_fontStyleStack.Add(FontStyles.LowerCase); return true; case -1668324918: // case -1883544150: // if ((baseConfiguration.fontStyle & FontStyles.LowerCase) != FontStyles.LowerCase) { - if (textConfiguration.m_fontStyleStack.Remove(FontStyles.LowerCase) == 0) - textConfiguration.m_fontStyleInternal &= ~FontStyles.LowerCase; + if (textConfigurationStack.m_fontStyleStack.Remove(FontStyles.LowerCase) == 0) + textConfigurationStack.m_fontStyleInternal &= ~FontStyles.LowerCase; } return true; case 13526026: // case 9133802: // case 781906058: // case 566686826: // - textConfiguration.m_fontStyleInternal |= FontStyles.UpperCase; - textConfiguration.m_fontStyleStack.Add(FontStyles.UpperCase); + textConfigurationStack.m_fontStyleInternal |= FontStyles.UpperCase; + textConfigurationStack.m_fontStyleStack.Add(FontStyles.UpperCase); return true; case 52232547: // case 47840323: // @@ -1249,21 +1263,21 @@ internal static bool ValidateHtmlTag( case -1831660941: // if ((baseConfiguration.fontStyle & FontStyles.UpperCase) != FontStyles.UpperCase) { - if (textConfiguration.m_fontStyleStack.Remove(FontStyles.UpperCase) == 0) - textConfiguration.m_fontStyleInternal &= ~FontStyles.UpperCase; + if (textConfigurationStack.m_fontStyleStack.Remove(FontStyles.UpperCase) == 0) + textConfigurationStack.m_fontStyleInternal &= ~FontStyles.UpperCase; } return true; case 766244328: // case 551025096: // - textConfiguration.m_fontStyleInternal |= FontStyles.SmallCaps; - textConfiguration.m_fontStyleStack.Add(FontStyles.SmallCaps); + textConfigurationStack.m_fontStyleInternal |= FontStyles.SmallCaps; + textConfigurationStack.m_fontStyleStack.Add(FontStyles.SmallCaps); return true; case -1632103439: // case -1847322671: // if ((baseConfiguration.fontStyle & FontStyles.SmallCaps) != FontStyles.SmallCaps) { - if (textConfiguration.m_fontStyleStack.Remove(FontStyles.SmallCaps) == 0) - textConfiguration.m_fontStyleInternal &= ~FontStyles.SmallCaps; + if (textConfigurationStack.m_fontStyleStack.Remove(FontStyles.SmallCaps) == 0) + textConfigurationStack.m_fontStyleInternal &= ~FontStyles.SmallCaps; } return true; case 2109854: // @@ -1272,27 +1286,27 @@ internal static bool ValidateHtmlTag( switch (firstTagIndentifier.valueType) { case TagValueType.NumericalValue: - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; // Determine tag unit type switch (tagUnitType) { case TagUnitType.Pixels: - textConfiguration.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textConfigurationStack.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: - textConfiguration.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textConfigurationStack.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfigurationStack.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.m_marginLeft = - (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; + textConfigurationStack.m_marginLeft = + (textConfigurationStack.m_marginWidth - (textConfigurationStack.m_width != -1 ? textConfigurationStack.m_width : 0)) * value / 100; break; } - textConfiguration.m_marginLeft = textConfiguration.m_marginLeft >= 0 ? textConfiguration.m_marginLeft : 0; - textConfiguration.m_marginRight = textConfiguration.m_marginLeft; + textConfigurationStack.m_marginLeft = textConfigurationStack.m_marginLeft >= 0 ? textConfigurationStack.m_marginLeft : 0; + textConfigurationStack.m_marginRight = textConfigurationStack.m_marginLeft; return true; case TagValueType.None: @@ -1305,47 +1319,53 @@ internal static bool ValidateHtmlTag( switch (nameHashCode) { case 42823: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, currentTagIndentifier.valueStartIndex, currentTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, currentTagIndentifier.valueStartIndex, + currentTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (richTextTagIndentifiers[i].unitType) { case TagUnitType.Pixels: - textConfiguration.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textConfigurationStack.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: - textConfiguration.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textConfigurationStack.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * + textConfigurationStack.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.m_marginLeft = - (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; + textConfigurationStack.m_marginLeft = + (textConfigurationStack.m_marginWidth - + (textConfigurationStack.m_width != -1 ? textConfigurationStack.m_width : 0)) * value / 100; break; } - textConfiguration.m_marginLeft = textConfiguration.m_marginLeft >= 0 ? textConfiguration.m_marginLeft : 0; + textConfigurationStack.m_marginLeft = textConfigurationStack.m_marginLeft >= 0 ? textConfigurationStack.m_marginLeft : 0; break; case 315620: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, currentTagIndentifier.valueStartIndex, currentTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, currentTagIndentifier.valueStartIndex, + currentTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (richTextTagIndentifiers[i].unitType) { case TagUnitType.Pixels: - textConfiguration.m_marginRight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textConfigurationStack.m_marginRight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: - textConfiguration.m_marginRight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textConfigurationStack.m_marginRight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * + textConfigurationStack.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.m_marginRight = - (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; + textConfigurationStack.m_marginRight = + (textConfigurationStack.m_marginWidth - + (textConfigurationStack.m_width != -1 ? textConfigurationStack.m_width : 0)) * value / 100; break; } - textConfiguration.m_marginRight = textConfiguration.m_marginRight >= 0 ? textConfiguration.m_marginRight : 0; + textConfigurationStack.m_marginRight = textConfigurationStack.m_marginRight >= 0 ? textConfigurationStack.m_marginRight : 0; break; } } @@ -1355,67 +1375,67 @@ internal static bool ValidateHtmlTag( return false; case 7639357: // case 7011901: // - textConfiguration.m_marginLeft = 0; - textConfiguration.m_marginRight = 0; + textConfigurationStack.m_marginLeft = 0; + textConfigurationStack.m_marginRight = 0; return true; case 1100728678: // case -855002522: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) { case TagUnitType.Pixels: - textConfiguration.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textConfigurationStack.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: - textConfiguration.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textConfigurationStack.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfigurationStack.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.m_marginLeft = - (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; + textConfigurationStack.m_marginLeft = + (textConfigurationStack.m_marginWidth - (textConfigurationStack.m_width != -1 ? textConfigurationStack.m_width : 0)) * value / 100; break; } - textConfiguration.m_marginLeft = textConfiguration.m_marginLeft >= 0 ? textConfiguration.m_marginLeft : 0; + textConfigurationStack.m_marginLeft = textConfigurationStack.m_marginLeft >= 0 ? textConfigurationStack.m_marginLeft : 0; return true; case -884817987: // case -1690034531: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) { case TagUnitType.Pixels: - textConfiguration.m_marginRight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textConfigurationStack.m_marginRight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: - textConfiguration.m_marginRight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textConfigurationStack.m_marginRight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfigurationStack.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.m_marginRight = - (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; + textConfigurationStack.m_marginRight = + (textConfigurationStack.m_marginWidth - (textConfigurationStack.m_width != -1 ? textConfigurationStack.m_width : 0)) * value / 100; break; } - textConfiguration.m_marginRight = textConfiguration.m_marginRight >= 0 ? textConfiguration.m_marginRight : 0; + textConfigurationStack.m_marginRight = textConfigurationStack.m_marginRight >= 0 ? textConfigurationStack.m_marginRight : 0; return true; case 1109349752: // case -842693512: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; switch (tagUnitType) { case TagUnitType.Pixels: - textConfiguration.m_lineHeight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); + textConfigurationStack.m_lineHeight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); break; case TagUnitType.FontUnits: - textConfiguration.m_lineHeight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; + textConfigurationStack.m_lineHeight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfigurationStack.m_currentFontSize; break; case TagUnitType.Percentage: //fontScale = (richtextAdjustments.m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (richtextAdjustments.m_isOrthographic ? 1 : 0.1f)); @@ -1425,11 +1445,11 @@ internal static bool ValidateHtmlTag( return true; case -445573839: // case 1897350193: // - textConfiguration.m_lineHeight = float.MinValue; //TMP_Math.FLOAT_UNSET -->is there a better way to do this? + textConfigurationStack.m_lineHeight = float.MinValue; //TMP_Math.FLOAT_UNSET -->is there a better way to do this? return true; case 15115642: // case 10723418: // - textConfiguration.tag_NoParsing = true; + textConfigurationStack.m_tagNoParsing = true; return true; //case 1913798: // //case 1286342: // @@ -1458,32 +1478,32 @@ internal static bool ValidateHtmlTag( // return true; case 315682: // case 226050: // - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; - textConfiguration.m_FXScale = new Vector3(value, 1, 1); + textConfigurationStack.m_fxScale = new Vector3(value, 1, 1); return true; case 1105611: // case 1015979: // - textConfiguration.m_FXScale = 1; + textConfigurationStack.m_fxScale = 1; return true; case 2227963: // case 1600507: // // TODO: Add ability to use Random Rotation - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + calliString.GetSubString(ref textConfigurationStack.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. - if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) + if (ConvertToFloat(ref textConfigurationStack.m_htmlTag, out value) != ParseError.None) return false; - textConfiguration.m_FXRotationAngle = math.radians(value); + textConfigurationStack.m_fxRotationAngleCCW = -math.radians(value); return true; case 7757466: // case 7130010: // - textConfiguration.m_FXRotationAngle = 0; + textConfigurationStack.m_fxRotationAngleCCW = 0; return true; case 317446: // case 227814: //
diff --git a/Calligraphics/Internal/TextConfiguration.cs b/Calligraphics/Internal/TextConfiguration.cs index 82cae89..19b0604 100644 --- a/Calligraphics/Internal/TextConfiguration.cs +++ b/Calligraphics/Internal/TextConfiguration.cs @@ -4,16 +4,66 @@ using UnityEngine; using UnityEngine.TextCore.Text; +// As a general rule, GlyphGeneration should never modify the ActiveTextConfiguration or TextConfigurationStack, only read them. +// They should be driven by the RichTextParser. + namespace Latios.Calligraphics { - internal struct TextConfiguration + internal struct TextGenerationStateCommands { - /// - /// m_htmlTag is a scratchpad for storing substrings prior to parsing. - /// Would not be needed if CalliString.SubString() would work...does not for some reason - /// (error that underlying Callibyte buffer is null) - /// - public FixedString128Bytes m_htmlTag; + public float xAdvanceChange; + public bool xAdvanceIsOverwrite; // False is additive + + public void Reset() + { + xAdvanceChange = 0f; + xAdvanceIsOverwrite = false; + } + } + + internal struct ActiveTextConfiguration + { + public float m_fontScaleMultiplier; // Used for handling of superscript and subscript. + public float m_currentFontSize; + public FontStyles m_fontStyleInternal; + //public FontWeight m_fontWeightInternal; + public int m_currentFontMaterialIndex; + public HorizontalAlignmentOptions m_lineJustification; + public float m_baselineOffset; + public Color32 m_htmlColor; + //public Color32 m_underlineColor; + //public Color32 m_strikethroughColor; + public short m_italicAngle; + public float m_lineOffset; + //public float m_lineHeight; + public float m_cSpacing; + public float m_monoSpacing; + //public float m_xAdvance; + //public float m_tagLineIndent; + //public float m_tagIndent; + //public float m_marginWidth; + //public float m_marginHeight; + //public float m_marginLeft; + //public float m_marginRight; + //public float m_width; + //public bool m_isNonBreakingSpace; + public float m_fxRotationAngleCCW; + public float3 m_fxScale; + + //// The following are derived values + //public float baseScale; + // + //public void CalculateDerived(in TextBaseConfiguration baseConfiguration, ref FontBlob font) + //{ + // baseScale = m_currentFontSize / font.pointSize * font.scale * (baseConfiguration.isOrthographic ? 1 : 0.1f); + //} + } + + internal struct TextConfigurationStack + { + // These top two are scratchpads for RichTextParser. + public FixedString128Bytes m_htmlTag; + public FixedList512Bytes richTextTagIndentifiers; //metrics public float m_fontScaleMultiplier; // Used for handling of superscript and subscript. @@ -50,12 +100,11 @@ internal struct TextConfiguration public float m_cSpacing; public float m_monoSpacing; - public float m_xAdvance; - public float tag_LineIndent; - public float tag_Indent; + public float m_tagLineIndent; + public float m_tagIndent; public FixedStack512Bytes m_indentStack; - public bool tag_NoParsing; + public bool m_tagNoParsing; public float m_marginWidth; public float m_marginHeight; @@ -67,65 +116,63 @@ internal struct TextConfiguration public bool m_isParsingText; - public float m_FXRotationAngle; - public float3 m_FXScale; + public float m_fxRotationAngleCCW; + public float3 m_fxScale; public FixedStack512Bytes m_highlightStateStack; - public int m_characterCount; - public TextConfiguration(TextBaseConfiguration textBaseConfiguration) + public void Reset(TextBaseConfiguration textBaseConfiguration) { - m_htmlTag = new FixedString128Bytes(); + m_htmlTag.Clear(); m_fontScaleMultiplier = 1; m_currentFontSize = textBaseConfiguration.fontSize; - m_sizeStack = new FixedStack512Bytes(); + m_sizeStack.Clear(); m_sizeStack.Add(m_currentFontSize); m_fontStyleInternal = textBaseConfiguration.fontStyle; m_fontWeightInternal = (m_fontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : textBaseConfiguration.fontWeight; - m_fontWeightStack = new FixedStack512Bytes(); + m_fontWeightStack.Clear(); m_fontWeightStack.Add(m_fontWeightInternal); - m_fontStyleStack = new FontStyleStack(); + m_fontStyleStack.Clear(); m_currentFontMaterialIndex = 0; - m_fontMaterialIndexStack = new FixedStack512Bytes(); + m_fontMaterialIndexStack.Clear(); m_fontMaterialIndexStack.Add(0); - m_lineJustification = textBaseConfiguration.lineJustification; - m_lineJustificationStack = new FixedStack512Bytes(); + m_lineJustification = textBaseConfiguration.lineJustification; + m_lineJustificationStack.Clear(); m_lineJustificationStack.Add(m_lineJustification); - m_baselineOffset = 0; - m_baselineOffsetStack = new FixedStack512Bytes(); + m_baselineOffset = 0; + m_baselineOffsetStack.Clear(); m_baselineOffsetStack.Add(0); m_htmlColor = textBaseConfiguration.color; m_underlineColor = Color.white; m_strikethroughColor = Color.white; - m_colorStack = new FixedStack512Bytes(); + m_colorStack.Clear(); m_colorStack.Add(m_htmlColor); - m_underlineColorStack = new FixedStack512Bytes(); + m_underlineColorStack.Clear(); m_underlineColorStack.Add(m_htmlColor); - m_strikethroughColorStack = new FixedStack512Bytes(); + m_strikethroughColorStack.Clear(); m_strikethroughColorStack.Add(m_htmlColor); - m_italicAngle = 0; - m_italicAngleStack = new FixedStack512Bytes(); + m_italicAngle = 0; + m_italicAngleStack.Clear(); m_lineOffset = 0; // Amount of space between lines (font line spacing + m_linespacing). m_lineHeight = float.MinValue; //TMP_Math.FLOAT_UNSET -->is there a better way to do this? m_cSpacing = 0; // Amount of space added between characters as a result of the use of the tag. m_monoSpacing = 0; - m_xAdvance = 0; // Used to track the position of each character. - tag_LineIndent = 0; // Used for indentation of text. - tag_Indent = 0; - m_indentStack = new FixedStack512Bytes(); - m_indentStack.Add(tag_Indent); - tag_NoParsing = false; + m_tagLineIndent = 0; // Used for indentation of text. + m_tagIndent = 0; + m_indentStack.Clear(); + m_indentStack.Add(m_tagIndent); + m_tagNoParsing = false; m_marginWidth = 0; m_marginHeight = 0; @@ -135,13 +182,44 @@ public TextConfiguration(TextBaseConfiguration textBaseConfiguration) m_isNonBreakingSpace = false; - m_isParsingText = false; - m_FXRotationAngle = 0; - m_FXScale = 1; + m_isParsingText = false; + m_fxRotationAngleCCW = 0; + m_fxScale = 1; - m_highlightStateStack = new FixedStack512Bytes(); + m_highlightStateStack.Clear(); + } - m_characterCount = 0; // Total characters in the CalliString + public ActiveTextConfiguration GetActiveConfiguration() + { + return new ActiveTextConfiguration + { + m_baselineOffset = m_baselineOffset, + m_cSpacing = m_cSpacing, + m_currentFontMaterialIndex = m_currentFontMaterialIndex, + m_currentFontSize = m_currentFontSize, + m_fontScaleMultiplier = m_fontScaleMultiplier, + m_fontStyleInternal = m_fontStyleInternal, + //m_fontWeightInternal = m_fontWeightInternal, + m_fxRotationAngleCCW = m_fxRotationAngleCCW, + m_fxScale = m_fxScale, + m_htmlColor = m_htmlColor, + //m_isNonBreakingSpace = m_isNonBreakingSpace, + m_italicAngle = m_italicAngle, + //m_lineHeight = m_lineHeight, + m_lineJustification = m_lineJustification, + m_lineOffset = m_lineOffset, + //m_marginHeight = m_marginHeight, + //m_marginLeft = m_marginLeft, + //m_marginRight = m_marginRight, + //m_marginWidth = m_marginWidth, + m_monoSpacing = m_monoSpacing, + //m_strikethroughColor = m_strikethroughColor, + //m_underlineColor = m_underlineColor, + //m_width = m_width, + //m_xAdvance = m_xAdvance, + //m_tagIndent = m_tagIndent, + //m_tagLineIndent = m_tagLineIndent, + }; } } } diff --git a/Calligraphics/Systems/AnimateTextTransitionSystem.cs b/Calligraphics/Systems/AnimateTextTransitionSystem.cs index c1e5a3b..fee92fa 100644 --- a/Calligraphics/Systems/AnimateTextTransitionSystem.cs +++ b/Calligraphics/Systems/AnimateTextTransitionSystem.cs @@ -137,11 +137,11 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE endIndex = -1; break; case TransitionTextUnitScope.Word: - startIndex = glyphMapper.GetGlyphStartIndexAndCountForWord(transition.startIndex).x; - if (transition.endIndex >= glyphMapper.wordCount - 1) - { + startIndex = glyphMapper.GetGlyphStartIndexAndCountForWord(math.min(transition.startIndex, glyphMapper.wordCount - 1)).x; + if (transition.endIndex >= glyphMapper.wordCount) + endIndex = -1; + else if (transition.endIndex == glyphMapper.wordCount - 1) endIndex = renderGlyphs.Length - 1; - } else if (transition.endIndex == transition.startIndex) endIndex = glyphMapper.GetGlyphStartIndexAndCountForWord(transition.endIndex + 1).x - 1; else @@ -149,11 +149,11 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE break; case TransitionTextUnitScope.Line: - startIndex = glyphMapper.GetGlyphStartIndexAndCountForLine(transition.startIndex).x; - if (transition.endIndex >= glyphMapper.lineCount - 1) - { + startIndex = glyphMapper.GetGlyphStartIndexAndCountForLine(math.min(transition.startIndex, glyphMapper.lineCount - 1)).x; + if (transition.endIndex >= glyphMapper.lineCount) + endIndex = -1; + else if (transition.endIndex == glyphMapper.lineCount - 1) endIndex = renderGlyphs.Length - 1; - } else if (transition.endIndex == transition.startIndex) endIndex = glyphMapper.GetGlyphStartIndexAndCountForLine(transition.endIndex + 1).x - 1; else diff --git a/Calligraphics/Systems/GenerateGlyphsSystem.cs b/Calligraphics/Systems/GenerateGlyphsSystem.cs index cdfd327..925fec9 100644 --- a/Calligraphics/Systems/GenerateGlyphsSystem.cs +++ b/Calligraphics/Systems/GenerateGlyphsSystem.cs @@ -92,7 +92,8 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE var additionalEntitiesBuffers = chunk.GetBufferAccessor(ref additionalEntitiesHandle); bool hasMultipleFonts = selectorBuffers.Length > 0 && additionalEntitiesBuffers.Length > 0; - FontMaterialSet fontMaterialSet = default; + FontMaterialSet fontMaterialSet = default; + TextConfigurationStack textConfigurationStack = default; for (int indexInChunk = 0; indexInChunk < chunk.Count; indexInChunk++) { @@ -115,6 +116,7 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE GlyphGeneration.CreateRenderGlyphs(ref renderGlyphs, ref m_glyphMappingWriter, ref fontMaterialSet, + ref textConfigurationStack, in calliBytes, in textBaseConfiguration); diff --git a/Calligraphics/Systems/Rendering/GpuResidentTextDispatchSystem.cs b/Calligraphics/Systems/Rendering/GpuResidentTextDispatchSystem.cs index f2bf98f..be3c8c4 100644 --- a/Calligraphics/Systems/Rendering/GpuResidentTextDispatchSystem.cs +++ b/Calligraphics/Systems/Rendering/GpuResidentTextDispatchSystem.cs @@ -23,6 +23,8 @@ namespace Latios.Calligraphics.Rendering.Systems [DisableAutoCreation] public partial class GpuResidentTextDispatchSystem : CullingComputeDispatchSubSystemBase { + DynamicComponentTypeHandle m_gpuUpdateFlagDynamicHandle; + ComputeShader m_uploadGlyphsShader; ComputeShader m_uploadMasksShader; @@ -30,7 +32,7 @@ public partial class GpuResidentTextDispatchSystem : CullingComputeDispatchSubSy EntityQuery m_newMasksQuery; EntityQuery m_changedGlyphsQuery; EntityQuery m_changedMasksQuery; - EntityQuery m_changedGlyphsWithMasksQuery; + EntityQuery m_newAndChangedGlyphsWithMasksQuery; EntityQuery m_deadQuery; EntityQuery m_allQuery; EntityQuery m_newQuery; @@ -43,6 +45,7 @@ public partial class GpuResidentTextDispatchSystem : CullingComputeDispatchSubSy int _dst; int _startOffset; int _meta; + int _elementSizeInBytes; int _latiosTextBuffer; int _latiosTextMaskBuffer; @@ -54,6 +57,8 @@ public partial class GpuResidentTextDispatchSystem : CullingComputeDispatchSubSy protected override void OnCreate() { + m_gpuUpdateFlagDynamicHandle = GetDynamicComponentTypeHandle(ComponentType.ReadOnly()); + m_newGlyphsQuery = Fluent.With(true) .With( true) .With( false) @@ -68,15 +73,14 @@ protected override void OnCreate() m_changedMasksQuery = Fluent.With(true) .With(false) .WithEnabled(true).Build(); - m_changedGlyphsWithMasksQuery = Fluent.With(true) - .With(true) - .With( false) - .WithEnabled(true).Build(); + m_newAndChangedGlyphsWithMasksQuery = Fluent.With(true) + .With(true) + .With( false).Build(); m_deadQuery = Fluent.With(true).Without().Build(); m_allQuery = Fluent.WithAnyEnabled(true) - .With(true).Build(); + .With( true).Build(); m_newQuery = Fluent.With(true) - .With( true) + .With( true) .WithAnyEnabled(true) .Without().Build(); @@ -86,6 +90,7 @@ protected override void OnCreate() _dst = Shader.PropertyToID("_dst"); _startOffset = Shader.PropertyToID("_startOffset"); _meta = Shader.PropertyToID("_meta"); + _elementSizeInBytes = Shader.PropertyToID("_elementSizeInBytes"); _latiosTextBuffer = Shader.PropertyToID("_latiosTextBuffer"); _latiosTextMaskBuffer = Shader.PropertyToID("_latiosTextMaskBuffer"); @@ -93,7 +98,7 @@ protected override void OnCreate() m_maskGaps = new NativeList(Allocator.Persistent); worldBlackboardEntity.AddComponent(); - worldBlackboardEntity.AddComponent(); + worldBlackboardEntity.AddComponentData(new GpuResidentMaskCount { maskCount = 1}); if (!worldBlackboardEntity.HasManagedStructComponent()) throw new System.InvalidOperationException("Calligraphics must be installed after Kinemation."); @@ -233,7 +238,7 @@ protected override IEnumerable UpdatePhase() var maskPayloads = new NativeList(1, WorldUpdateAllocator); var requiredMaskUploadBufferSize = new NativeReference(WorldUpdateAllocator, NativeArrayOptions.UninitializedMemory); var maskStreamCount = CollectionHelper.CreateNativeArray(1, WorldUpdateAllocator); - maskStreamCount[0] = m_changedMasksQuery.CalculateChunkCountWithoutFiltering(); + maskStreamCount[0] = m_newMasksQuery.CalculateChunkCountWithoutFiltering(); isSingle = maskStreamCount[0] == 0; if (isSingle) maskStreamCount[0] = 1; @@ -264,12 +269,15 @@ protected override IEnumerable UpdatePhase() requiredUploadBufferSize = requiredMaskUploadBufferSize, }.Schedule(collectMasksJh); + m_gpuUpdateFlagDynamicHandle.Update(this); var copyPropertiesJh = new CopyGlyphShaderIndicesJob { - additionalEntitiesHandle = SystemAPI.GetBufferTypeHandle(true), - shaderIndexHandle = SystemAPI.GetComponentTypeHandle(true), - shaderIndexLookup = SystemAPI.GetComponentLookup(false), - }.ScheduleParallel(m_changedGlyphsWithMasksQuery, collectGlyphsJh); + additionalEntitiesHandle = SystemAPI.GetBufferTypeHandle(true), + shaderIndexHandle = SystemAPI.GetComponentTypeHandle(true), + renderGlyphMaskLookup = SystemAPI.GetBufferLookup(true), + gpuResidentUpdateFlagHandle = m_gpuUpdateFlagDynamicHandle, + shaderIndexLookup = SystemAPI.GetComponentLookup(false), + }.ScheduleParallel(m_newAndChangedGlyphsWithMasksQuery, collectGlyphsJh); // Cleanup var accb = latiosWorld.syncPoint.CreateAddComponentsCommandBuffer(AddComponentsDestroyedEntityResolution.AddToNewEntityAndDestroy); @@ -374,6 +382,7 @@ protected override IEnumerable UpdatePhase() m_uploadMasksShader.SetBuffer(0, _dst, persistentMaskBuffer); m_uploadMasksShader.SetBuffer(0, _src, maskUploadBuffer); m_uploadMasksShader.SetBuffer(0, _meta, maskMetaBuffer); + m_uploadMasksShader.SetInt(_elementSizeInBytes, 4); for (uint dispatchesRemaining = (uint)maskPayloads.Length, offset = 0; dispatchesRemaining > 0;) { @@ -668,10 +677,14 @@ unsafe void AddNewAllocations(in ArchetypeChunk chunk, bool useEnabledMask, v128 uint maskCountToStore = (uint)buffer.Length; uint firstIndex = GapAllocation.Allocate(maskGaps, maskCountToStore, ref gpuResidentMaskCount); - var alloc = allocations[i]; - alloc.maskStart = firstIndex; - alloc.maskCount = maskCountToStore; - allocations[i] = alloc; + if (allocations.Length > 0) + { + var alloc = allocations[i]; + alloc.maskStart = firstIndex; + alloc.maskCount = maskCountToStore; + allocations[i] = alloc; + } + shaderIndices[i] = new TextMaterialMaskShaderIndex { firstMaskIndex = firstIndex, @@ -696,19 +709,33 @@ struct CopyGlyphShaderIndicesJob : IJobChunk { [ReadOnly] public ComponentTypeHandle shaderIndexHandle; [ReadOnly] public BufferTypeHandle additionalEntitiesHandle; + [ReadOnly] public BufferLookup renderGlyphMaskLookup; + [ReadOnly] public DynamicComponentTypeHandle gpuResidentUpdateFlagHandle; [NativeDisableContainerSafetyRestriction] public ComponentLookup shaderIndexLookup; public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { var additionalEntitiesBuffers = chunk.GetBufferAccessor(ref additionalEntitiesHandle); var shaderIndices = chunk.GetNativeArray(ref shaderIndexHandle); + var enabledMask = chunkEnabledMask; + + if (chunk.Has(ref gpuResidentUpdateFlagHandle)) + { + useEnabledMask = true; + enabledMask = chunk.GetEnableableBits(ref gpuResidentUpdateFlagHandle); + if (enabledMask.ULong0 == 0 && enabledMask.ULong1 == 0) + return; + } - var enumerator = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count); + var enumerator = new ChunkEntityEnumerator(useEnabledMask, enabledMask, chunk.Count); while (enumerator.NextEntityIndex(out int i)) { + var indices = shaderIndices[i]; foreach (var entity in additionalEntitiesBuffers[i]) { - shaderIndexLookup[entity.entity] = shaderIndices[i]; + var maskBuffer = renderGlyphMaskLookup[entity.entity]; + indices.glyphCount = (uint)(16 * maskBuffer.Length); + shaderIndexLookup[entity.entity] = indices; } } } diff --git a/Calligraphics/Systems/Rendering/TextRenderingDispatchSystem.cs b/Calligraphics/Systems/Rendering/TextRenderingDispatchSystem.cs index f8f8008..62462e4 100644 --- a/Calligraphics/Systems/Rendering/TextRenderingDispatchSystem.cs +++ b/Calligraphics/Systems/Rendering/TextRenderingDispatchSystem.cs @@ -35,6 +35,7 @@ public partial class TextRenderingDispatchSystem : CullingComputeDispatchSubSyst int _dst; int _startOffset; int _meta; + int _elementSizeInBytes; int _latiosTextBuffer; int _latiosTextMaskBuffer; @@ -55,6 +56,7 @@ protected override void OnCreate() .Without().Build(); m_glyphsAndMasksQuery = Fluent.With(true) .With(true) + .With( true) .With( true, true).Without().Build(); var copyByteAddressShader = Resources.Load("CopyBytes"); @@ -64,6 +66,7 @@ protected override void OnCreate() _dst = Shader.PropertyToID("_dst"); _startOffset = Shader.PropertyToID("_startOffset"); _meta = Shader.PropertyToID("_meta"); + _elementSizeInBytes = Shader.PropertyToID("_elementSizeInBytes"); _latiosTextBuffer = Shader.PropertyToID("_latiosTextBuffer"); _latiosTextMaskBuffer = Shader.PropertyToID("_latiosTextMaskBuffer"); @@ -188,6 +191,7 @@ protected override IEnumerable UpdatePhase() perCameraMaskHandle = SystemAPI.GetComponentTypeHandle(true), perFrameMaskHandle = SystemAPI.GetComponentTypeHandle(true), shaderIndexHandle = SystemAPI.GetComponentTypeHandle(true), + renderGlyphMaskLookup = SystemAPI.GetBufferLookup(true), shaderIndexLookup = SystemAPI.GetComponentLookup(false) }.ScheduleParallel(m_glyphsAndMasksQuery, collectGlyphsJh); @@ -280,7 +284,7 @@ protected override IEnumerable UpdatePhase() m_uploadGlyphsShader.Dispatch(0, (int)dispatchCount, 1, 1); offset += dispatchCount; dispatchesRemaining -= dispatchCount; - } + } Shader.SetGlobalBuffer(_latiosTextBuffer, persistentGlyphBuffer); var frameMaskCount = worldBlackboardEntity.GetComponentData().maskCount; @@ -292,6 +296,7 @@ protected override IEnumerable UpdatePhase() m_uploadMasksShader.SetBuffer(0, _dst, persistentMaskBuffer); m_uploadMasksShader.SetBuffer(0, _src, maskUploadBuffer); m_uploadMasksShader.SetBuffer(0, _meta, maskMetaBuffer); + m_uploadMasksShader.SetInt(_elementSizeInBytes, 4); for (uint dispatchesRemaining = (uint)maskPayloads.Length, offset = 0; dispatchesRemaining > 0;) { @@ -556,10 +561,10 @@ struct GatherMaskUploadOperationsJob : IJobChunk public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { - var cameraMask = chunk.GetChunkComponentData(ref perCameraMaskHandle); - var frameMask = chunk.GetChunkComponentData(ref perFrameMaskHandle); - var lower = cameraMask.lower.Value & (~frameMask.lower.Value); - var upper = cameraMask.upper.Value & (~frameMask.upper.Value); + var cameraMask = chunk.GetChunkComponentData(ref perCameraMaskHandle); + var frameMask = chunk.GetChunkComponentData(ref perFrameMaskHandle); + var lower = cameraMask.lower.Value & (~frameMask.lower.Value); + var upper = cameraMask.upper.Value & (~frameMask.upper.Value); if ((upper | lower) == 0) return; @@ -603,14 +608,15 @@ struct CopyGlyphShaderIndicesJob : IJobChunk [ReadOnly] public ComponentTypeHandle perCameraMaskHandle; [ReadOnly] public ComponentTypeHandle shaderIndexHandle; [ReadOnly] public BufferTypeHandle additionalEntitiesHandle; + [ReadOnly] public BufferLookup renderGlyphMaskLookup; [NativeDisableContainerSafetyRestriction] public ComponentLookup shaderIndexLookup; public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { var cameraMask = chunk.GetChunkComponentData(ref perCameraMaskHandle); - var frameMask = chunk.GetChunkComponentData(ref perFrameMaskHandle); - var lower = cameraMask.lower.Value & (~frameMask.lower.Value); - var upper = cameraMask.upper.Value & (~frameMask.upper.Value); + var frameMask = chunk.GetChunkComponentData(ref perFrameMaskHandle); + var lower = cameraMask.lower.Value & (~frameMask.lower.Value); + var upper = cameraMask.upper.Value & (~frameMask.upper.Value); if ((upper | lower) == 0) return; @@ -620,9 +626,12 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo var enumerator = new ChunkEntityEnumerator(true, new v128(lower, upper), chunk.Count); while (enumerator.NextEntityIndex(out int i)) { + var indices = shaderIndices[i]; foreach (var entity in additionalEntitiesBuffers[i]) { - shaderIndexLookup[entity.entity] = shaderIndices[i]; + var maskBuffer = renderGlyphMaskLookup[entity.entity]; + indices.glyphCount = (uint)(16 * maskBuffer.Length); + shaderIndexLookup[entity.entity] = indices; } } } diff --git a/Calligraphics/Systems/Rendering/TextRenderingUpdateSystem.cs b/Calligraphics/Systems/Rendering/TextRenderingUpdateSystem.cs index 20e2709..3f09ee4 100644 --- a/Calligraphics/Systems/Rendering/TextRenderingUpdateSystem.cs +++ b/Calligraphics/Systems/Rendering/TextRenderingUpdateSystem.cs @@ -198,14 +198,17 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo for (int entityIndex = 0; entityIndex < chunk.Count; entityIndex++) { var entity = entities[entityIndex]; + instances.Clear(); var ctrl = controlLookup[entity]; if ((ctrl.flags & TextRenderControl.Flags.Dirty) != TextRenderControl.Flags.Dirty) continue; ctrl.flags &= ~TextRenderControl.Flags.Dirty; - var gpuBit = gpuResidentAllocationLookup.GetComponentEnabledRefRWOptional(entity); - if (gpuBit.IsValid) - gpuBit.ValueRW = true; + { + var gpuBit = gpuResidentAllocationLookup.GetComponentEnabledRefRWOptional(entity); + if (gpuBit.IsValid) + gpuBit.ValueRW = true; + } var glyphBuffer = glyphBuffers[entityIndex].AsNativeArray(); var selectorBuffer = selectorBuffers[entityIndex].AsNativeArray().Reinterpret(); @@ -227,6 +230,10 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo entity = entityBuffer[i] }); } + foreach (var instance in instances) + { + instance.masks.Clear(); + } var glyphCount = math.min(glyphBuffer.Length, selectorBuffer.Length); for (int i = 0; i < glyphCount; i++) @@ -259,6 +266,10 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo { ref var instance = ref instances.ElementAt(i); + var gpuBit = gpuResidentAllocationLookup.GetComponentEnabledRefRWOptional(instance.entity); + if (gpuBit.IsValid) + gpuBit.ValueRW = true; + Physics.GetCenterExtents(instance.aabb, out var center, out var extents); if (glyphBuffer.Length == 0) { diff --git a/Kinemation/Systems/Culling/GenerateBrgDrawCommandsSystem.cs b/Kinemation/Systems/Culling/GenerateBrgDrawCommandsSystem.cs index 20b01bb..7fae81b 100644 --- a/Kinemation/Systems/Culling/GenerateBrgDrawCommandsSystem.cs +++ b/Kinemation/Systems/Culling/GenerateBrgDrawCommandsSystem.cs @@ -494,6 +494,260 @@ private void EmitDrawCommand(in DrawCommandSettings settings, int entityQword, i } } +#elif !LATIOS_TRANSFORMS_UNCACHED_QVVS && LATIOS_TRANSFORMS_UNITY + [BurstCompile] + unsafe struct EmitDrawCommandsJob : IJobParallelForDefer + { + [ReadOnly] public NativeArray chunksToProcess; + [ReadOnly] public ComponentTypeHandle chunkPerCameraCullingMaskHandle; + [ReadOnly] public ComponentTypeHandle chunkPerCameraCullingSplitsMaskHandle; + [ReadOnly] public ComponentTypeHandle lodCrossfadeHandle; + [ReadOnly] public ComponentTypeHandle speedTreeCrossfadeTagHandle; + [ReadOnly] public EntityQueryMask motionVectorDeformQueryMask; + public bool splitsAreValid; + + //[ReadOnly] public IndirectList VisibilityItems; + [ReadOnly] public ComponentTypeHandle EntitiesGraphicsChunkInfo; + [ReadOnly] public ComponentTypeHandle MaterialMeshInfo; + [ReadOnly] public ComponentTypeHandle WorldTransform; + [ReadOnly] public ComponentTypeHandle PostProcessMatrix; + [ReadOnly] public ComponentTypeHandle DepthSorted; + [ReadOnly] public SharedComponentTypeHandle RenderMeshArray; + [ReadOnly] public SharedComponentTypeHandle RenderFilterSettings; + [ReadOnly] public SharedComponentTypeHandle LightMaps; + [ReadOnly] public NativeParallelHashMap BRGRenderMeshArrays; + + public ChunkDrawCommandOutput DrawCommandOutput; + + public ulong SceneCullingMask; + public float3 CameraPosition; + public uint LastSystemVersion; + public uint CullingLayerMask; + + public ProfilerMarker ProfilerEmitChunk; + +#if UNITY_EDITOR + [ReadOnly] public SharedComponentTypeHandle EditorDataComponentHandle; +#endif + + public void Execute(int i) + { + Execute(chunksToProcess[i]); + } + + void Execute(in ArchetypeChunk chunk) + { + //var visibilityItem = VisibilityItems.ElementAt(index); + + //var chunkVisibility = visibilityItem.Visibility; + + int filterIndex = chunk.GetSharedComponentIndex(RenderFilterSettings); + + DrawCommandOutput.InitializeForEmitThread(); + + { + var entitiesGraphicsChunkInfo = chunk.GetChunkComponentData(ref EntitiesGraphicsChunkInfo); + + if (!entitiesGraphicsChunkInfo.Valid) + return; + + // If the chunk has a RenderMeshArray, get access to the corresponding registered + // Material and Mesh IDs + BRGRenderMeshArray brgRenderMeshArray = default; + if (!BRGRenderMeshArrays.IsEmpty) + { + int renderMeshArrayIndex = chunk.GetSharedComponentIndex(RenderMeshArray); + bool hasRenderMeshArray = renderMeshArrayIndex >= 0; + if (hasRenderMeshArray) + BRGRenderMeshArrays.TryGetValue(renderMeshArrayIndex, out brgRenderMeshArray); + } + + ref var chunkCullingData = ref entitiesGraphicsChunkInfo.CullingData; + + int batchIndex = entitiesGraphicsChunkInfo.BatchIndex; + + var materialMeshInfos = chunk.GetNativeArray(ref MaterialMeshInfo); + var worldTransforms = chunk.GetNativeArray(ref WorldTransform); + var postProcessMatrices = chunk.GetNativeArray(ref PostProcessMatrix); + bool hasPostProcess = chunk.Has(ref PostProcessMatrix); + bool isDepthSorted = chunk.Has(ref DepthSorted); + bool isLightMapped = chunk.GetSharedComponentIndex(LightMaps) >= 0; + bool hasLodCrossfade = chunk.Has(ref lodCrossfadeHandle); + + // Check if the chunk has statically disabled motion (i.e. never in motion pass) + // or enabled motion (i.e. in motion pass if there was actual motion or force-to-zero). + // We make sure to never set the motion flag if motion is statically disabled to improve batching + // in cases where the transform is changed. + bool hasMotion = (chunkCullingData.Flags & EntitiesGraphicsChunkCullingData.kFlagPerObjectMotion) != 0; + + if (hasMotion) + { + bool orderChanged = chunk.DidOrderChange(LastSystemVersion); + bool transformChanged = chunk.DidChange(ref WorldTransform, LastSystemVersion); + if (hasPostProcess) + transformChanged |= chunk.DidChange(ref PostProcessMatrix, LastSystemVersion); + bool isDeformed = motionVectorDeformQueryMask.MatchesIgnoreFilter(chunk); + hasMotion = orderChanged || transformChanged || isDeformed; + } + + int chunkStartIndex = entitiesGraphicsChunkInfo.CullingData.ChunkOffsetInBatch; + + var mask = chunk.GetChunkComponentRefRO(ref chunkPerCameraCullingMaskHandle); + var splitsMask = chunk.GetChunkComponentRefRO(ref chunkPerCameraCullingSplitsMaskHandle); + var crossFadeEnableds = hasLodCrossfade ? chunk.GetEnabledMask(ref lodCrossfadeHandle) : default; + var isSpeedTree = hasLodCrossfade && chunk.Has(ref speedTreeCrossfadeTagHandle); + + float4x4* depthSortingTransformsPtr = null; + if (isDepthSorted && hasPostProcess) + { + // In this case, we don't actually have a component that represents the rendered position. + // So we allocate a new array and compute the world positions. We store them in TransformQvvs + // so that the read pointer looks the same as our WorldTransforms. + // We compute them in the inner loop since only the visible instances are read from later, + // and it is a lot cheaper to only compute the visible instances. + var allocator = DrawCommandOutput.ThreadLocalAllocator.ThreadAllocator(DrawCommandOutput.ThreadIndex)->Handle; + depthSortingTransformsPtr = AllocatorManager.Allocate(allocator, chunk.Count); + } + else if (isDepthSorted) + { + depthSortingTransformsPtr = (float4x4*)worldTransforms.GetUnsafeReadOnlyPtr(); + } + + for (int j = 0; j < 2; j++) + { + ulong visibleWord = mask.ValueRO.GetUlongFromIndex(j); + + while (visibleWord != 0) + { + int bitIndex = math.tzcnt(visibleWord); + int entityIndex = (j << 6) + bitIndex; + ulong entityMask = 1ul << bitIndex; + + // Clear the bit first in case we early out from the loop + visibleWord ^= entityMask; + + MaterialMeshInfo materialMeshInfo = materialMeshInfos[entityIndex]; + BatchID batchID = new BatchID { value = (uint)batchIndex }; + ushort splitMask = splitsAreValid ? splitsMask.ValueRO.splitMasks[entityIndex] : (ushort)0; // Todo: Should the default be 1 instead of 0? + bool flipWinding = (chunkCullingData.FlippedWinding[j] & entityMask) != 0; + + BatchDrawCommandFlags drawCommandFlags = 0; + + if (flipWinding) + drawCommandFlags |= BatchDrawCommandFlags.FlipWinding; + + if (hasMotion) + drawCommandFlags |= BatchDrawCommandFlags.HasMotion; + + if (isLightMapped) + drawCommandFlags |= BatchDrawCommandFlags.IsLightMapped; + + if (hasLodCrossfade && crossFadeEnableds[entityIndex]) + { + // Todo: Remove the LODCrossFade line and replace with the commented out lines in Unity 6. + drawCommandFlags |= BatchDrawCommandFlags.LODCrossFade; + //drawCommandFlags |= BatchDrawCommandFlags.LODCrossFadeValuePacked; + //if (!isSpeedTree) + // drawCommandFlags |= BatchDrawCommandFlags.LODCrossFadeKeyword; + } + + // Depth sorted draws are emitted with access to entity transforms, + // so they can also be written out for sorting + if (isDepthSorted) + { + drawCommandFlags |= BatchDrawCommandFlags.HasSortingPosition; + // To maintain compatibility with most of the data structures, we pretend we have a LocalToWorld matrix pointer. + // We also customize the code where this pointer is read. + if (hasPostProcess) + { + var index = j * 64 + bitIndex; + var f4x4 = new float4x4(new float4(postProcessMatrices[index].postProcessMatrix.c0, 0f), + new float4(postProcessMatrices[index].postProcessMatrix.c1, 0f), + new float4(postProcessMatrices[index].postProcessMatrix.c2, 0f), + new float4(postProcessMatrices[index].postProcessMatrix.c3, 1f)); + depthSortingTransformsPtr[index].c3.xyz = math.transform(f4x4, worldTransforms[index].Position); + } + } + + if (materialMeshInfo.HasMaterialMeshIndexRange) + { + RangeInt matMeshIndexRange = materialMeshInfo.MaterialMeshIndexRange; + + for (int i = 0; i < matMeshIndexRange.length; i++) + { + int matMeshSubMeshIndex = matMeshIndexRange.start + i; + + // Drop the draw command if OOB. Errors should have been reported already so no need to log anything + if (matMeshSubMeshIndex >= brgRenderMeshArray.MaterialMeshSubMeshes.Length) + continue; + + BatchMaterialMeshSubMesh matMeshSubMesh = brgRenderMeshArray.MaterialMeshSubMeshes[matMeshSubMeshIndex]; + + DrawCommandSettings settings = new DrawCommandSettings + { + FilterIndex = filterIndex, + BatchID = batchID, + MaterialID = matMeshSubMesh.Material, + MeshID = matMeshSubMesh.Mesh, + SplitMask = splitMask, + SubMeshIndex = (ushort)matMeshSubMesh.SubMeshIndex, + Flags = drawCommandFlags + }; + + EmitDrawCommand(settings, j, bitIndex, chunkStartIndex, depthSortingTransformsPtr); + } + } + else + { + BatchMeshID meshID = materialMeshInfo.IsRuntimeMesh ? + materialMeshInfo.MeshID : + brgRenderMeshArray.GetMeshID(materialMeshInfo); + + // Invalid meshes at this point will be skipped. + if (meshID == BatchMeshID.Null) + continue; + + // Null materials are handled internally by Unity using the error material if available. + BatchMaterialID materialID = materialMeshInfo.IsRuntimeMaterial ? + materialMeshInfo.MaterialID : + brgRenderMeshArray.GetMaterialID(materialMeshInfo); + + if (materialID == BatchMaterialID.Null) + continue; + + var settings = new DrawCommandSettings + { + FilterIndex = filterIndex, + BatchID = batchID, + MaterialID = materialID, + MeshID = meshID, + SplitMask = splitMask, + SubMeshIndex = (ushort)materialMeshInfo.SubMesh, + Flags = drawCommandFlags + }; + + EmitDrawCommand(settings, j, bitIndex, chunkStartIndex, depthSortingTransformsPtr); + } + } + } + } + } + + private void EmitDrawCommand(in DrawCommandSettings settings, int entityQword, int entityBit, int chunkStartIndex, void* depthSortingPtr) + { + // Depth sorted draws are emitted with access to entity transforms, + // so they can also be written out for sorting + if (settings.HasSortingPosition) + { + DrawCommandOutput.EmitDepthSorted(settings, entityQword, entityBit, chunkStartIndex, (float4x4*)depthSortingPtr); + } + else + { + DrawCommandOutput.Emit(settings, entityQword, entityBit, chunkStartIndex); + } + } + } +#endif [BurstCompile] internal unsafe struct ExpandVisibleInstancesJob : IJobParallelForDefer { @@ -744,256 +998,6 @@ private int ExpandVisibilityWithPositionsCrossfade( return numInstances; } } -#endif - -#if !LATIOS_TRANSFORMS_UNCACHED_QVVS && LATIOS_TRANSFORMS_UNITY - [BurstCompile] - unsafe struct EmitDrawCommandsJob : IJobParallelForDefer - { - [ReadOnly] public NativeArray chunksToProcess; - [ReadOnly] public ComponentTypeHandle chunkPerCameraCullingMaskHandle; - [ReadOnly] public ComponentTypeHandle chunkPerCameraCullingSplitsMaskHandle; - [ReadOnly] public ComponentTypeHandle lodCrossfadeHandle; - public bool splitsAreValid; - - //[ReadOnly] public IndirectList VisibilityItems; - [ReadOnly] public ComponentTypeHandle EntitiesGraphicsChunkInfo; - [ReadOnly] public ComponentTypeHandle MaterialMeshInfo; - [ReadOnly] public ComponentTypeHandle WorldTransform; - [ReadOnly] public ComponentTypeHandle PostProcessMatrix; - [ReadOnly] public ComponentTypeHandle DepthSorted; - [ReadOnly] public SharedComponentTypeHandle RenderMeshArray; - [ReadOnly] public SharedComponentTypeHandle RenderFilterSettings; - [ReadOnly] public SharedComponentTypeHandle LightMaps; - [ReadOnly] public NativeParallelHashMap BRGRenderMeshArrays; - - public ChunkDrawCommandOutput DrawCommandOutput; - - public ulong SceneCullingMask; - public float3 CameraPosition; - public uint LastSystemVersion; - public uint CullingLayerMask; - - public ProfilerMarker ProfilerEmitChunk; - -#if UNITY_EDITOR - [ReadOnly] public SharedComponentTypeHandle EditorDataComponentHandle; -#endif - - public void Execute(int i) - { - Execute(chunksToProcess[i]); - } - - void Execute(in ArchetypeChunk chunk) - { - //var visibilityItem = VisibilityItems.ElementAt(index); - - //var chunkVisibility = visibilityItem.Visibility; - - int filterIndex = chunk.GetSharedComponentIndex(RenderFilterSettings); - - DrawCommandOutput.InitializeForEmitThread(); - - { - var entitiesGraphicsChunkInfo = chunk.GetChunkComponentData(ref EntitiesGraphicsChunkInfo); - - if (!entitiesGraphicsChunkInfo.Valid) - return; - - // If the chunk has a RenderMeshArray, get access to the corresponding registered - // Material and Mesh IDs - BRGRenderMeshArray brgRenderMeshArray = default; - if (!BRGRenderMeshArrays.IsEmpty) - { - int renderMeshArrayIndex = chunk.GetSharedComponentIndex(RenderMeshArray); - bool hasRenderMeshArray = renderMeshArrayIndex >= 0; - if (hasRenderMeshArray) - BRGRenderMeshArrays.TryGetValue(renderMeshArrayIndex, out brgRenderMeshArray); - } - - ref var chunkCullingData = ref entitiesGraphicsChunkInfo.CullingData; - - int batchIndex = entitiesGraphicsChunkInfo.BatchIndex; - - var materialMeshInfos = chunk.GetNativeArray(ref MaterialMeshInfo); - var worldTransforms = chunk.GetNativeArray(ref WorldTransform); - var postProcessMatrices = chunk.GetNativeArray(ref PostProcessMatrix); - bool hasPostProcess = chunk.Has(ref PostProcessMatrix); - bool isDepthSorted = chunk.Has(ref DepthSorted); - bool isLightMapped = chunk.GetSharedComponentIndex(LightMaps) >= 0; - bool hasLodCrossfade = chunk.Has(ref lodCrossfadeHandle); - - // Check if the chunk has statically disabled motion (i.e. never in motion pass) - // or enabled motion (i.e. in motion pass if there was actual motion or force-to-zero). - // We make sure to never set the motion flag if motion is statically disabled to improve batching - // in cases where the transform is changed. - bool hasMotion = (chunkCullingData.Flags & EntitiesGraphicsChunkCullingData.kFlagPerObjectMotion) != 0; - - if (hasMotion) - { - bool orderChanged = chunk.DidOrderChange(LastSystemVersion); - bool transformChanged = chunk.DidChange(ref WorldTransform, LastSystemVersion); - if (hasPostProcess) - transformChanged |= chunk.DidChange(ref PostProcessMatrix, LastSystemVersion); -#if ENABLE_DOTS_DEFORMATION_MOTION_VECTORS - bool isDeformed = chunk.Has(ref DeformedMeshIndex); -#else - bool isDeformed = false; -#endif - hasMotion = orderChanged || transformChanged || isDeformed; - } - - int chunkStartIndex = entitiesGraphicsChunkInfo.CullingData.ChunkOffsetInBatch; - - var mask = chunk.GetChunkComponentRefRO(ref chunkPerCameraCullingMaskHandle); - var splitsMask = chunk.GetChunkComponentRefRO(ref chunkPerCameraCullingSplitsMaskHandle); - - float4x4* depthSortingTransformsPtr = null; - if (isDepthSorted && hasPostProcess) - { - // In this case, we don't actually have a component that represents the rendered position. - // So we allocate a new array and compute the world positions. We store them in TransformQvvs - // so that the read pointer looks the same as our WorldTransforms. - // We compute them in the inner loop since only the visible instances are read from later, - // and it is a lot cheaper to only compute the visible instances. - var allocator = DrawCommandOutput.ThreadLocalAllocator.ThreadAllocator(DrawCommandOutput.ThreadIndex)->Handle; - depthSortingTransformsPtr = AllocatorManager.Allocate(allocator, chunk.Count); - } - else if (isDepthSorted) - { - depthSortingTransformsPtr = (float4x4*)worldTransforms.GetUnsafeReadOnlyPtr(); - } - - for (int j = 0; j < 2; j++) - { - ulong visibleWord = mask.ValueRO.GetUlongFromIndex(j); - - while (visibleWord != 0) - { - int bitIndex = math.tzcnt(visibleWord); - int entityIndex = (j << 6) + bitIndex; - ulong entityMask = 1ul << bitIndex; - - // Clear the bit first in case we early out from the loop - visibleWord ^= entityMask; - - MaterialMeshInfo materialMeshInfo = materialMeshInfos[entityIndex]; - BatchID batchID = new BatchID { value = (uint)batchIndex }; - ushort splitMask = splitsAreValid ? splitsMask.ValueRO.splitMasks[entityIndex] : (ushort)0; // Todo: Should the default be 1 instead of 0? - bool flipWinding = (chunkCullingData.FlippedWinding[j] & entityMask) != 0; - - BatchDrawCommandFlags drawCommandFlags = 0; - - if (flipWinding) - drawCommandFlags |= BatchDrawCommandFlags.FlipWinding; - - if (hasMotion) - drawCommandFlags |= BatchDrawCommandFlags.HasMotion; - - if (isLightMapped) - drawCommandFlags |= BatchDrawCommandFlags.IsLightMapped; - - if (hasLodCrossfade) - drawCommandFlags |= BatchDrawCommandFlags.LODCrossFade; - - // Depth sorted draws are emitted with access to entity transforms, - // so they can also be written out for sorting - if (isDepthSorted) - { - drawCommandFlags |= BatchDrawCommandFlags.HasSortingPosition; - // To maintain compatibility with most of the data structures, we pretend we have a LocalToWorld matrix pointer. - // We also customize the code where this pointer is read. - if (hasPostProcess) - { - var index = j * 64 + bitIndex; - var f4x4 = new float4x4(new float4(postProcessMatrices[index].postProcessMatrix.c0, 0f), - new float4(postProcessMatrices[index].postProcessMatrix.c1, 0f), - new float4(postProcessMatrices[index].postProcessMatrix.c2, 0f), - new float4(postProcessMatrices[index].postProcessMatrix.c3, 1f)); - depthSortingTransformsPtr[index].c3.xyz = math.transform(f4x4, worldTransforms[index].Position); - } - } - - if (materialMeshInfo.HasMaterialMeshIndexRange) - { - RangeInt matMeshIndexRange = materialMeshInfo.MaterialMeshIndexRange; - - for (int i = 0; i < matMeshIndexRange.length; i++) - { - int matMeshSubMeshIndex = matMeshIndexRange.start + i; - - // Drop the draw command if OOB. Errors should have been reported already so no need to log anything - if (matMeshSubMeshIndex >= brgRenderMeshArray.MaterialMeshSubMeshes.Length) - continue; - - BatchMaterialMeshSubMesh matMeshSubMesh = brgRenderMeshArray.MaterialMeshSubMeshes[matMeshSubMeshIndex]; - - DrawCommandSettings settings = new DrawCommandSettings - { - FilterIndex = filterIndex, - BatchID = batchID, - MaterialID = matMeshSubMesh.Material, - MeshID = matMeshSubMesh.Mesh, - SplitMask = splitMask, - SubMeshIndex = (ushort)matMeshSubMesh.SubMeshIndex, - Flags = drawCommandFlags - }; - - EmitDrawCommand(settings, j, bitIndex, chunkStartIndex, depthSortingTransformsPtr); - } - } - else - { - BatchMeshID meshID = materialMeshInfo.IsRuntimeMesh ? - materialMeshInfo.MeshID : - brgRenderMeshArray.GetMeshID(materialMeshInfo); - - // Invalid meshes at this point will be skipped. - if (meshID == BatchMeshID.Null) - continue; - - // Null materials are handled internally by Unity using the error material if available. - BatchMaterialID materialID = materialMeshInfo.IsRuntimeMaterial ? - materialMeshInfo.MaterialID : - brgRenderMeshArray.GetMaterialID(materialMeshInfo); - - if (materialID == BatchMaterialID.Null) - continue; - - var settings = new DrawCommandSettings - { - FilterIndex = filterIndex, - BatchID = batchID, - MaterialID = materialID, - MeshID = meshID, - SplitMask = splitMask, - SubMeshIndex = (ushort)materialMeshInfo.SubMesh, - Flags = drawCommandFlags - }; - - EmitDrawCommand(settings, j, bitIndex, chunkStartIndex, depthSortingTransformsPtr); - } - } - } - } - } - - private void EmitDrawCommand(in DrawCommandSettings settings, int entityQword, int entityBit, int chunkStartIndex, void* depthSortingPtr) - { - // Depth sorted draws are emitted with access to entity transforms, - // so they can also be written out for sorting - if (settings.HasSortingPosition) - { - DrawCommandOutput.EmitDepthSorted(settings, entityQword, entityBit, chunkStartIndex, (float4x4*)depthSortingPtr); - } - else - { - DrawCommandOutput.Emit(settings, entityQword, entityBit, chunkStartIndex); - } - } - } -#endif [BurstCompile] unsafe struct GenerateDrawCommandsJob : IJobParallelForDefer diff --git a/README.md b/README.md index 140cfc2..5d31628 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ ![](https://github.com/Dreaming381/Latios-Framework-Documentation/blob/554a583e217bfe5bf38ece0ed65b22c33711afc6/media/bf2cb606139bb3ca01fe1c4c9f92cdf7.png) -# Latios Framework for Unity ECS – [0.10.0-beta.1] +# Latios Framework for Unity ECS – [0.10.0-beta.2] **This is a prerelease version of the Latios Framework version 0.10 which is still under development. Changelogs and Documentation are currently being -updated to reflect the new features and changes in 0.9.** +updated to reflect the new features and changes in 0.10.** **You are still welcome to submit bug reports and PRs for this and future prerelease versions!** diff --git a/Transforms/UnityTransforms/TransformSuperSystems.cs b/Transforms/UnityTransforms/TransformSuperSystems.cs index 9442141..a66cb7c 100644 --- a/Transforms/UnityTransforms/TransformSuperSystems.cs +++ b/Transforms/UnityTransforms/TransformSuperSystems.cs @@ -20,17 +20,14 @@ protected override void CreateSystems() } } - [DisableAutoCreation] [UpdateInGroup(typeof(Latios.Systems.LatiosWorldSyncGroup))] public partial class GameObjectEntityBindingSystem : SubSystem { } - [DisableAutoCreation] [UpdateInGroup(typeof(TransformSystemGroup), OrderFirst = true)] public partial struct CopyGameObjectTransformToEntitySystem : ISystem {} - [DisableAutoCreation] [UpdateInGroup(typeof(TransformSystemGroup), OrderLast = true)] public partial struct CopyGameObjectTransformFromEntitySystem : ISystem {} } diff --git a/package.json b/package.json index b663251..a911587 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.latios.latiosframework", "displayName": "Latios Framework for ECS", - "version": "0.10.0-beta.1", + "version": "0.10.0-beta.2", "unity": "2022.3", "description": "Latios Framework for ECS is a collection of tools, algorithms, and API extensions developed by a hardcore hobbyist game developer.\n\nThis package includes all of the following modules:\n\u25aa Core\n\u25aa QVVS Transforms\n\u25aa Psyshock Physics\n\u25aa Myri Audio\n\u25aa Kinemation Animation and Rendering\nu25aa Caligraphics\nu25aa Mimic\n\nExamples: \n\u25aa Latios Space Shooter Sample - https://github.com/Dreaming381/lsss-wip \n\u25aa Mini Demos - https://github.com/Dreaming381/LatiosFrameworkMiniDemos \n\u25aa Free Parking - https://github.com/Dreaming381/Free-Parking", "documentationURL": "https://github.com/Dreaming381/Latios-Framework-Documentation",