diff --git a/Calligraphics/Authoring/BakingSystems/FontSmartBlobberSystem.cs b/Calligraphics/Authoring/BakingSystems/FontSmartBlobberSystem.cs index b390279..8ac3d44 100644 --- a/Calligraphics/Authoring/BakingSystems/FontSmartBlobberSystem.cs +++ b/Calligraphics/Authoring/BakingSystems/FontSmartBlobberSystem.cs @@ -99,31 +99,32 @@ protected override void OnUpdate() }).WithEntityQueryOptions(EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabledEntities).WithoutBurst().Run(); } - public static BlobAssetReference BakeFont(FontAsset font, Material material) + public static unsafe BlobAssetReference BakeFont(FontAsset font, Material material) { float materialPadding = material.GetPaddingForText(false, false); var builder = new BlobBuilder(Allocator.Temp); - ref FontBlob FontBlobFont = ref builder.ConstructRoot(); - FontBlobFont.scale = font.faceInfo.scale; - FontBlobFont.pointSize = font.faceInfo.pointSize; - FontBlobFont.baseLine = font.faceInfo.baseline; - FontBlobFont.ascentLine = font.faceInfo.ascentLine; - FontBlobFont.descentLine = font.faceInfo.descentLine; - FontBlobFont.lineHeight = font.faceInfo.lineHeight; - FontBlobFont.regularStyleSpacing = font.regularStyleSpacing; - FontBlobFont.regularStyleWeight = font.regularStyleWeight; - FontBlobFont.boldStyleSpacing = font.boldStyleSpacing; - FontBlobFont.boldStyleWeight = font.boldStyleWeight; - FontBlobFont.italicsStyleSlant = font.italicStyleSlant; - FontBlobFont.capLine = font.faceInfo.capLine; - FontBlobFont.atlasWidth = font.atlasWidth; - FontBlobFont.atlasHeight = font.atlasHeight; - FontBlobFont.materialPadding = materialPadding; - - var glyphPairAdjustments = font.GetGlyphPairAdjustmentRecordLookup(); - - BlobBuilderArray glyphBuilder = builder.Allocate(ref FontBlobFont.characters, font.characterTable.Count); + ref FontBlob fontBlobRoot = ref builder.ConstructRoot(); + fontBlobRoot.scale = font.faceInfo.scale; + fontBlobRoot.pointSize = font.faceInfo.pointSize; + fontBlobRoot.baseLine = font.faceInfo.baseline; + fontBlobRoot.ascentLine = font.faceInfo.ascentLine; + fontBlobRoot.descentLine = font.faceInfo.descentLine; + fontBlobRoot.lineHeight = font.faceInfo.lineHeight; + fontBlobRoot.regularStyleSpacing = font.regularStyleSpacing; + fontBlobRoot.regularStyleWeight = font.regularStyleWeight; + fontBlobRoot.boldStyleSpacing = font.boldStyleSpacing; + fontBlobRoot.boldStyleWeight = font.boldStyleWeight; + fontBlobRoot.italicsStyleSlant = font.italicStyleSlant; + fontBlobRoot.capLine = font.faceInfo.capLine; + fontBlobRoot.atlasWidth = font.atlasWidth; + fontBlobRoot.atlasHeight = font.atlasHeight; + fontBlobRoot.materialPadding = materialPadding; + + var glyphPairAdjustments = font.GetGlyphPairAdjustmentRecordLookup(); + Span hashCounts = stackalloc int[64]; + hashCounts.Clear(); + BlobBuilderArray glyphBuilder = builder.Allocate(ref fontBlobRoot.characters, font.characterTable.Count); for (int i = 0; i < font.characterTable.Count; i++) { var character = font.characterTable[i]; @@ -185,7 +186,7 @@ public static BlobAssetReference BakeFont(FontAsset font, Material mat } //Get vertices and uvs - var vertices = GetVertices(character.glyph.metrics, materialPadding, font.atlasPadding / 2f, FontBlobFont.baseScale); + var vertices = GetVertices(character.glyph.metrics, materialPadding, font.atlasPadding / 2f, fontBlobRoot.baseScale); BlobBuilderArray verticesBuilder = builder.Allocate(ref glyphBlob.vertices, vertices.Length); for (int j = 0; j < vertices.Length; j++) { @@ -206,18 +207,43 @@ public static BlobAssetReference BakeFont(FontAsset font, Material mat uv2Builder[j] = uv2s[j]; } - glyphBuilder[i] = glyphBlob; + hashCounts[BlobTextMeshGlyphExtensions.GetGlyphHash(glyphBlob.unicode)]++; } } + var hashes = builder.Allocate(ref fontBlobRoot.glyphLookupMap, 64); + Span hashArrays = stackalloc HashArray[64]; + for (int i = 0; i < hashes.Length; i++) + { + hashArrays[i] = new HashArray + { + hashArray = (GlyphLookup*)builder.Allocate(ref hashes[i], hashCounts[i]).GetUnsafePtr() + }; + hashCounts[i] = 0; + } + + for (int i = 0; i < glyphBuilder.Length; i++) + { + if (glyphBuilder[i].unicode == 0) // Is this the right way to rule out null glyphs? + continue; + var hash = BlobTextMeshGlyphExtensions.GetGlyphHash(glyphBuilder[i].unicode); + hashArrays[hash].hashArray[hashCounts[hash]] = new GlyphLookup { unicode = glyphBuilder[i].unicode, index = i }; + hashCounts[hash]++; + } + var result = builder.CreateBlobAssetReference(Allocator.Persistent); builder.Dispose(); - FontBlobFont = result.Value; + fontBlobRoot = result.Value; return result; } + unsafe struct HashArray + { + public GlyphLookup* hashArray; + } + private static FixedList64Bytes GetVertices(GlyphMetrics glyphMetrics, float materialPadding, float stylePadding, float currentElementScale) { float2 topLeft; diff --git a/Calligraphics/Authoring/TextRendererAuthoring.cs b/Calligraphics/Authoring/TextRendererAuthoring.cs index e6812d2..7c32870 100644 --- a/Calligraphics/Authoring/TextRendererAuthoring.cs +++ b/Calligraphics/Authoring/TextRendererAuthoring.cs @@ -1,8 +1,9 @@ +using System; +using System.Collections.Generic; using Latios.Authoring; +using Latios.Calligraphics.Rendering; using Latios.Calligraphics.Rendering.Authoring; using Latios.Kinemation.Authoring; -using System; -using System.Collections.Generic; using Unity.Collections; using Unity.Entities; using Unity.Entities.Graphics; @@ -21,14 +22,14 @@ public class TextRendererAuthoring : MonoBehaviour [TextArea(5, 10)] public string text; - public float fontSize = 12f; - public bool wordWrap = true; - public float maxLineWidth = float.MaxValue; + public float fontSize = 12f; + public bool wordWrap = true; + public float maxLineWidth = float.MaxValue; public HorizontalAlignmentOptions horizontalAlignment = HorizontalAlignmentOptions.Left; - public VerticalAlignmentOptions verticalAlignment = VerticalAlignmentOptions.Top; - public bool isOrthographic; - public FontStyles fontStyle; - public FontWeight fontWeight; + public VerticalAlignmentOptions verticalAlignment = VerticalAlignmentOptions.Top; + public bool isOrthographic; + public FontStyles fontStyle; + public FontWeight fontWeight; public Color32 color = UnityEngine.Color.white; @@ -39,7 +40,7 @@ public class TextRendererAuthoring : MonoBehaviour public struct FontMaterialPair { public FontAsset font; - public Material overrideMaterial; + public Material overrideMaterial; public Material material => overrideMaterial == null ? font.material : overrideMaterial; } @@ -58,11 +59,15 @@ public override void Bake(TextRendererAuthoring authoring) AddFontRendering(entity, authoring.fontsAndMaterials[0]); if (authoring.fontsAndMaterials.Count > 1) { - var additionalEntities = AddBuffer(entity).Reinterpret(); + AddComponent(entity); + AddBuffer(entity); + var additionalEntities = AddBuffer(entity).Reinterpret(); for (int i = 1; i < authoring.fontsAndMaterials.Count; i++) { var newEntity = CreateAdditionalEntity(TransformUsageFlags.Renderable); AddFontRendering(newEntity, authoring.fontsAndMaterials[i]); + AddComponent(newEntity); + AddBuffer(newEntity); additionalEntities.Add(newEntity); } } @@ -72,14 +77,14 @@ public override void Bake(TextRendererAuthoring authoring) calliString.Append(authoring.text); AddComponent(entity, new TextBaseConfiguration { - fontSize = authoring.fontSize, - color = authoring.color, - maxLineWidth = math.select(float.MaxValue, authoring.maxLineWidth, authoring.wordWrap), + fontSize = authoring.fontSize, + color = authoring.color, + maxLineWidth = math.select(float.MaxValue, authoring.maxLineWidth, authoring.wordWrap), lineJustification = authoring.horizontalAlignment, verticalAlignment = authoring.verticalAlignment, - isOrthographic = authoring.isOrthographic, - fontStyle = authoring.fontStyle, - fontWeight = authoring.fontWeight, + isOrthographic = authoring.isOrthographic, + fontStyle = authoring.fontStyle, + fontWeight = authoring.fontWeight, }); } @@ -92,16 +97,16 @@ void AddFontRendering(Entity entity, FontMaterialPair fontMaterialPair) var layer = GetLayer(); this.BakeTextBackendMeshAndMaterial(new MeshRendererBakeSettings { - targetEntity = entity, + targetEntity = entity, renderMeshDescription = new RenderMeshDescription { FilterSettings = new RenderFilterSettings { - Layer = layer, + Layer = layer, RenderingLayerMask = (uint)(1 << layer), - ShadowCastingMode = ShadowCastingMode.Off, - ReceiveShadows = false, - MotionMode = MotionVectorGenerationMode.Object, + ShadowCastingMode = ShadowCastingMode.Off, + ReceiveShadows = false, + MotionMode = MotionVectorGenerationMode.Object, StaticShadowCaster = false, }, LightProbeUsage = LightProbeUsage.Off, diff --git a/Calligraphics/Components/FontBlob.cs b/Calligraphics/Components/FontBlob.cs index 5788ec1..a99a24d 100644 --- a/Calligraphics/Components/FontBlob.cs +++ b/Calligraphics/Components/FontBlob.cs @@ -1,3 +1,4 @@ +using Unity.Collections; using Unity.Entities; using Unity.Mathematics; using UnityEngine.TextCore.LowLevel; @@ -7,12 +8,13 @@ namespace Latios.Calligraphics //TODO: Underlay, Bold, Smallcaps public struct FontBlob { - public BlobArray characters; - public float ascentLine; - public float descentLine; - public float lineHeight; - public float pointSize; - public float scale; + public BlobArray characters; + public BlobArray > glyphLookupMap; + public float ascentLine; + public float descentLine; + public float lineHeight; + public float pointSize; + public float scale; public float baseLine; public float atlasWidth; @@ -22,7 +24,7 @@ public struct FontBlob public float regularStyleWeight; public float boldStyleSpacing; public float boldStyleWeight; - public byte italicsStyleSlant; + public byte italicsStyleSlant; public float capLine; @@ -40,14 +42,14 @@ public struct FontBlob public float baseScale => 1f / pointSize * scale * .1f; - public const float smallCapsScaleMultiplier = .8f; + public const float smallCapsScaleMultiplier = .8f; public const float orthographicScaleMultiplier = 10f; } public struct GlyphBlob { - public uint glyphIndex; - public uint unicode; + public uint glyphIndex; + public uint unicode; public BlobArray vertices; public BlobArray uv; public BlobArray uv2; @@ -84,13 +86,13 @@ public struct GlyphBlob public struct AdjustmentPair { public FontFeatureLookupFlags fontFeatureLookupFlags; - public GlyphAdjustment firstAdjustment; - public GlyphAdjustment secondAdjustment; + public GlyphAdjustment firstAdjustment; + public GlyphAdjustment secondAdjustment; } public struct GlyphAdjustment { - public uint glyphUnicode; + public uint glyphUnicode; public float xPlacement; public float yPlacement; public float xAdvance; @@ -100,29 +102,41 @@ public struct GlyphAdjustment { xPlacement = a.xPlacement + b.xPlacement, yPlacement = a.yPlacement + b.yPlacement, - xAdvance = a.xAdvance + b.xAdvance, - yAdvance = a.yAdvance + b.yAdvance, + xAdvance = a.xAdvance + b.xAdvance, + yAdvance = a.yAdvance + b.yAdvance, }; } } + public struct GlyphLookup + { + public uint unicode; + public int index; + } + public static class BlobTextMeshGlyphExtensions { - public static bool TryGetGlyph(ref this BlobArray glyphs, uint character, out int index) + public static bool TryGetGlyphIndex(ref this FontBlob font, uint character, out int index) { - index = -1; + ref var hashArray = ref font.glyphLookupMap[GetGlyphHash(character)]; + index = -1; - for (int i = 0; i < glyphs.Length; i++) + for (int i = 0; i < hashArray.Length; i++) { - if (glyphs[i].unicode == character) + if (hashArray[i].unicode == character) { - index = i; + index = hashArray[i].index; return true; } } return false; } + + public static int GetGlyphHash(uint unicode) + { + return (int)(unicode & 0x3f); + } } } diff --git a/Calligraphics/Components/TextComponents.cs b/Calligraphics/Components/TextComponents.cs index 37f16ea..58d58d7 100644 --- a/Calligraphics/Components/TextComponents.cs +++ b/Calligraphics/Components/TextComponents.cs @@ -34,36 +34,39 @@ public struct TextBaseConfiguration : IComponentData /// public Color32 color; /// - /// The horizontal alignment modes of the text + /// The horizontal alignment mode of the text /// - public HorizontalAlignmentOptions lineJustification; + public HorizontalAlignmentOptions lineJustification + { + get => (HorizontalAlignmentOptions)((m_alignmentWeightOrtho & 0x70) >> 4); + set => m_alignmentWeightOrtho = (ushort)((m_alignmentWeightOrtho & ~0x70) | ((ushort)value << 4)); + } /// - /// The horizontal alignment modes of the text + /// The vertical alignment mode of the text /// - public VerticalAlignmentOptions verticalAlignment; - public bool isOrthographic; - public FontStyles fontStyle; - public FontWeight fontWeight; - } - - /// - /// An additional rendered text entity containing a different font and material. - /// Currently unsupported. - /// - [InternalBufferCapacity(0)] - public struct AdditionalFontMaterialEntity : IBufferElementData - { - public EntityWith entity; - } + public VerticalAlignmentOptions verticalAlignment + { + get => (VerticalAlignmentOptions)((m_alignmentWeightOrtho & 0x380) >> 7); + set => m_alignmentWeightOrtho = (ushort)((m_alignmentWeightOrtho & ~0x380) | ((ushort)value << 7)); + } + public FontWeight fontWeight + { + get => (FontWeight)(m_alignmentWeightOrtho & 0x0f); + set => m_alignmentWeightOrtho = (ushort)((m_alignmentWeightOrtho & ~0x0f) | (ushort)value); + } + public FontStyles fontStyle + { + get => (FontStyles)m_fontStyleFlags; + set => m_fontStyleFlags = (ushort)value; + } + public bool isOrthographic + { + get => (m_alignmentWeightOrtho & 0x8000) != 0; + set => m_alignmentWeightOrtho = (ushort)((m_alignmentWeightOrtho & 0x7fff) | (value ? 0x8000 : 0)); + } - /// - /// A per-glyph index into the font and material that should be used to render it. - /// Currently unsupported. - /// - [InternalBufferCapacity(0)] - public struct FontMaterialSelectorForGlyph : IBufferElementData - { - public byte fontMaterialIndex; + private ushort m_fontStyleFlags; // 6 bits unused, but Unity may add more. + ushort m_alignmentWeightOrtho; // 5 bits unused. } /// @@ -111,19 +114,40 @@ public enum WriteMask : byte /// /// Horizontal text alignment options. /// - public enum HorizontalAlignmentOptions + public enum HorizontalAlignmentOptions : byte { - Left = 0x1, Center = 0x2, Right = 0x4, Justified = 0x8, Flush = 0x10, Geometry = 0x20 + Left, + Center, + Right, + Justified, + Flush, + Geometry } /// /// Vertical text alignment options. /// - public enum VerticalAlignmentOptions + public enum VerticalAlignmentOptions : byte { - Top = 0x100, Middle = 0x200, Bottom = 0x400, Baseline = 0x800, Geometry = 0x1000, Capline = 0x2000, + Top, + Middle, + Bottom, + Baseline, + Geometry, + Capline, } - public enum FontWeight { Thin = 100, ExtraLight = 200, Light = 300, Regular = 400, Medium = 500, SemiBold = 600, Bold = 700, Heavy = 800, Black = 900 }; + public enum FontWeight + { + Thin, + ExtraLight, + Light, + Regular, + Medium, + SemiBold, + Bold, + Heavy, + Black + }; } diff --git a/Calligraphics/Components/TextConfiguration.cs b/Calligraphics/Components/TextConfiguration.cs deleted file mode 100644 index 2ada362..0000000 --- a/Calligraphics/Components/TextConfiguration.cs +++ /dev/null @@ -1,139 +0,0 @@ -using Latios.Calligraphics; -using Unity.Collections; -using UnityEngine; -using UnityEngine.TextCore.Text; - -public struct TextConfiguration -{ - /// - /// 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; - - //metrics - public float m_fontScaleMultiplier; // Used for handling of superscript and subscript. - public float m_currentFontSize; - public FixedStack64Bytes m_sizeStack; - - public FontStyles m_FontStyleInternal; - public FontWeight m_FontWeightInternal; - public Calli_FontStyleStack m_fontStyleStack; - public FixedStack64Bytes m_FontWeightStack; - - public HorizontalAlignmentOptions m_lineJustification; - public FixedStack64Bytes m_lineJustificationStack; - - public float m_baselineOffset; - public FixedStack64Bytes m_baselineOffsetStack; - - public Color32 m_htmlColor; - public Color32 m_underlineColor; - public Color32 m_strikethroughColor; - - public FixedStack64Bytes m_colorStack; - public FixedStack64Bytes m_strikethroughColorStack; - public FixedStack64Bytes m_underlineColorStack; - - public short m_ItalicAngle; - public FixedStack64Bytes m_ItalicAngleStack; - - public float m_lineOffset; - public float m_lineHeight; - - public float m_cSpacing; - public float m_monoSpacing; - public float m_xAdvance; - - public float tag_LineIndent; - public float tag_Indent; - public FixedStack64Bytes m_indentStack; - public bool tag_NoParsing; - - 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 bool m_isParsingText; - - public Matrix4x4 m_FXMatrix; - public bool m_isFXMatrixSet; - - public FixedStack64Bytes m_HighlightStateStack; - public int m_characterCount; - - - public TextConfiguration(TextBaseConfiguration textBaseConfiguration) - { - m_htmlTag = new FixedString128Bytes(); - - m_fontScaleMultiplier = 1; - m_currentFontSize = textBaseConfiguration.fontSize; - m_sizeStack = new FixedStack64Bytes(); - m_sizeStack.Add(m_currentFontSize); - - - m_FontStyleInternal = textBaseConfiguration.fontStyle; - m_FontWeightInternal = (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : textBaseConfiguration.fontWeight; - m_FontWeightStack = new FixedStack64Bytes(); - m_FontWeightStack.Add(m_FontWeightInternal); - m_fontStyleStack = new Calli_FontStyleStack(); - - m_lineJustification = textBaseConfiguration.lineJustification; - m_lineJustificationStack = new FixedStack64Bytes(); - m_lineJustificationStack.Add(m_lineJustification); - - m_baselineOffset = 0; - m_baselineOffsetStack = new FixedStack64Bytes(); - m_baselineOffsetStack.Add(0); - - m_htmlColor = textBaseConfiguration.color; - m_underlineColor = Color.white; - m_strikethroughColor = Color.white; - - m_colorStack = new FixedStack64Bytes(); - m_colorStack.Add(m_htmlColor); - m_underlineColorStack = new FixedStack64Bytes(); - m_underlineColorStack.Add(m_htmlColor); - m_strikethroughColorStack = new FixedStack64Bytes(); - m_strikethroughColorStack.Add(m_htmlColor); - - m_ItalicAngle = 0; - m_ItalicAngleStack = new FixedStack64Bytes(); - - 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 FixedStack64Bytes(); - m_indentStack.Add(tag_Indent); - tag_NoParsing = false; - - m_marginWidth = 0; - m_marginHeight = 0; - m_marginLeft = 0; - m_marginRight = 0; - m_width = -1; - - m_isNonBreakingSpace = false; - - m_isParsingText = false; - m_FXMatrix = Matrix4x4.identity; - m_isFXMatrixSet = false; - - m_HighlightStateStack = new FixedStack64Bytes(); - - m_characterCount = 0; // Total characters in the CalliString - } -} - diff --git a/Calligraphics/Components/TextRenderingComponents.cs b/Calligraphics/Components/TextRenderingComponents.cs index 54b133f..87ba245 100644 --- a/Calligraphics/Components/TextRenderingComponents.cs +++ b/Calligraphics/Components/TextRenderingComponents.cs @@ -45,6 +45,13 @@ public struct TextShaderIndex : IComponentData public uint glyphCount; } + // Only present if there are child fonts + [MaterialProperty("_latiosTextGlyphMaskBase")] + public struct TextMaterialMaskShaderIndex : IComponentData + { + public uint firstMaskIndex; + } + public struct RenderGlyph : IBufferElementData { public float2 blPosition; @@ -132,9 +139,47 @@ public enum Flags : byte public Flags flags; } + /// + /// An additional rendered text entity containing a different font and material. + /// The additional entity shares the RenderGlyph buffer, and uses a mask to identify + /// the glyphs to render. + /// + [InternalBufferCapacity(0)] + public struct AdditionalFontMaterialEntity : IBufferElementData + { + public EntityWith entity; + } + + /// + /// A per-glyph index into the font and material that should be used to render it. + /// Index 0 is this entity. Index 1 is the first entity in AdditionalFontMaterialEntity buffer. + /// + [InternalBufferCapacity(0)] + public struct FontMaterialSelectorForGlyph : IBufferElementData + { + public byte fontMaterialIndex; + } + + /// + /// A buffer that should be present on every entity posessing or referenced by the + /// AdditionalFontMaterialEntity buffer. This buffer contains the GPU mask representation, + /// and its contents will automatically be maintained by the Calligraphics rendering backend. + /// Public so that you can add/remove it or maybe even read it (if you are brave). + /// + [InternalBufferCapacity(0)] + public struct RenderGlyphMask : IBufferElementData + { + public uint lowerOffsetUpperMask16; + } + internal struct GlyphCountThisFrame : IComponentData { public uint glyphCount; } + + internal struct MaskCountThisFrame : IComponentData + { + public uint maskCount; + } } diff --git a/Calligraphics/Editor/ExtraShaderGraphNodes/LatiosTextNode.cs b/Calligraphics/Editor/ExtraShaderGraphNodes/LatiosTextNode.cs index e08ab56..ff95089 100644 --- a/Calligraphics/Editor/ExtraShaderGraphNodes/LatiosTextNode.cs +++ b/Calligraphics/Editor/ExtraShaderGraphNodes/LatiosTextNode.cs @@ -96,6 +96,15 @@ public override void CollectShaderProperties(PropertyCollector properties, Gener hidden = true, value = Vector2.zero }); + properties.AddShaderProperty(new Vector1ShaderProperty() + { + displayName = $"Text Material Mask Index", + overrideReferenceName = "_latiosTextGlyphMaskBase", + overrideHLSLDeclaration = true, + hlslDeclarationOverride = HLSLDeclaration.HybridPerInstance, + hidden = true, + value = 0f + }); base.CollectShaderProperties(properties, generationMode); } @@ -113,7 +122,8 @@ public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMo if (generationMode == GenerationMode.ForReals) { sb.AppendLine("uint2 baseIndex = asuint(UNITY_ACCESS_HYBRID_INSTANCED_PROP(_latiosTextGlyphBase, float2));"); - sb.AppendLine("GlyphVertex glyph = sampleGlyph(IN.VertexID, baseIndex.x, baseIndex.y);"); + sb.AppendLine("uint maskIndex = asuint(UNITY_ACCESS_HYBRID_INSTANCED_PROP(_latiosTextGlyphMaskBase, float));"); // We rely on the default from AddShaderProperty + sb.AppendLine("GlyphVertex glyph = sampleGlyph(IN.VertexID, baseIndex.x, baseIndex.y, maskIndex);"); sb.AppendLine("{0} = glyph.position;", GetVariableNameForSlot(kPositionOutputSlotId)); sb.AppendLine("{0} = glyph.normal;", GetVariableNameForSlot(kNormalOutputSlotId)); sb.AppendLine("{0} = glyph.tangent;", GetVariableNameForSlot(kTangentOutputSlotId)); diff --git a/Calligraphics/Components/FixedStack64Bytes.cs b/Calligraphics/Internal/FixedStack512Bytes.cs similarity index 88% rename from Calligraphics/Components/FixedStack64Bytes.cs rename to Calligraphics/Internal/FixedStack512Bytes.cs index be8f100..bd17706 100644 --- a/Calligraphics/Components/FixedStack64Bytes.cs +++ b/Calligraphics/Internal/FixedStack512Bytes.cs @@ -2,10 +2,9 @@ namespace Latios.Calligraphics { - - public struct FixedStack64Bytes where T : unmanaged + internal struct FixedStack512Bytes where T : unmanaged { - FixedList64Bytes m_buffer; + FixedList512Bytes m_buffer; public bool IsEmpty => m_buffer.IsEmpty; public void Add(in T item) => m_buffer.Add(in item); public T Pop() diff --git a/Calligraphics/Components/FixedStack64Bytes.cs.meta b/Calligraphics/Internal/FixedStack512Bytes.cs.meta similarity index 100% rename from Calligraphics/Components/FixedStack64Bytes.cs.meta rename to Calligraphics/Internal/FixedStack512Bytes.cs.meta diff --git a/Calligraphics/Internal/FontMaterialSet.cs b/Calligraphics/Internal/FontMaterialSet.cs new file mode 100644 index 0000000..b4be09c --- /dev/null +++ b/Calligraphics/Internal/FontMaterialSet.cs @@ -0,0 +1,69 @@ +using Latios.Calligraphics.Rendering; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Entities; +using Unity.Mathematics; + +namespace Latios.Calligraphics +{ + internal unsafe struct FontMaterialSet + { + FixedList4096Bytes m_fontMaterialArray; + FixedList512Bytes m_fontToEntityIndexArray; + DynamicBuffer m_selectorBuffer; + bool m_hasMultipleFonts; + + public ref FontBlob this[int index] => ref m_fontMaterialArray[index].font; + + public void WriteFontMaterialIndexForGlyph(int index) + { + if (!m_hasMultipleFonts) + return; + var remap = m_fontToEntityIndexArray[index]; + m_selectorBuffer.Add(new FontMaterialSelectorForGlyph { fontMaterialIndex = remap }); + } + + public void Initialize(BlobAssetReference singleFont) + { + m_hasMultipleFonts = false; + m_fontMaterialArray.Clear(); + m_fontMaterialArray.Add(new FontMaterial(singleFont)); + } + + public void Initialize(BlobAssetReference baseFont, + DynamicBuffer selectorBuffer, + DynamicBuffer entities, + ref ComponentLookup blobLookup) + { + Initialize(baseFont); + m_selectorBuffer = selectorBuffer; + m_selectorBuffer.Clear(); + m_hasMultipleFonts = true; + m_fontToEntityIndexArray.Clear(); + for (int i = 0; i < entities.Length; i++) + { + if (blobLookup.TryGetComponent(entities[i].entity, out var blobRef)) + { + if (blobRef.blob.IsCreated) + { + m_fontMaterialArray.Add(new FontMaterial(blobRef.blob)); + m_fontToEntityIndexArray.Add((byte)i); + } + } + } + } + + unsafe struct FontMaterial + { + FontBlob* m_fontBlobPtr; + + public ref FontBlob font => ref *m_fontBlobPtr; + + public FontMaterial(BlobAssetReference blobRef) + { + m_fontBlobPtr = (FontBlob*)blobRef.GetUnsafePtr(); + } + } + } +} + diff --git a/Calligraphics/Internal/FontMaterialSet.cs.meta b/Calligraphics/Internal/FontMaterialSet.cs.meta new file mode 100644 index 0000000..68c5385 --- /dev/null +++ b/Calligraphics/Internal/FontMaterialSet.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7e2880bddb2695c4a9a5d556678802ba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Calligraphics/Internal/GlyphGeneration.cs b/Calligraphics/Internal/GlyphGeneration.cs index 286dc41..cb2eeb8 100644 --- a/Calligraphics/Internal/GlyphGeneration.cs +++ b/Calligraphics/Internal/GlyphGeneration.cs @@ -1,6 +1,5 @@ using Latios.Calligraphics.Rendering; using Latios.Calligraphics.RichText; -using Latios.Calligraphics.RichText.Parsing; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Entities; @@ -8,32 +7,33 @@ namespace Latios.Calligraphics { - public static class GlyphGeneration + internal static class GlyphGeneration { internal static unsafe void CreateRenderGlyphs(ref DynamicBuffer renderGlyphs, ref GlyphMappingWriter mappingWriter, - ref FontBlob font, - in DynamicBuffer calliBytes, + ref FontMaterialSet fontMaterialSet, + in DynamicBuffer calliBytes, in TextBaseConfiguration baseConfiguration) { renderGlyphs.Clear(); //initialized textConfiguration which stores all fields that are modified by RichText Tags - var richTextAttributes = new FixedList512Bytes(); - var textConfiguration = new TextConfiguration(baseConfiguration); + var richTextTagIdentifiers = new FixedList512Bytes(); + var textConfiguration = new TextConfiguration(baseConfiguration); - float2 cumulativeOffset = new float2(); // Tracks text progression and word wrap - float2 adjustmentOffset = new float2(); //Tracks placement adjustments - int characterCount = 0; - int lastWordStartCharacterGlyphIndex = 0; + float2 cumulativeOffset = new float2(); // Tracks text progression and word wrap + float2 adjustmentOffset = new float2(); //Tracks placement adjustments + int characterCount = 0; + int lastWordStartCharacterGlyphIndex = 0; FixedList512Bytes characterGlyphIndicesWithPreceedingSpacesInLine = default; - int accumulatedSpaces = 0; - int startOfLineGlyphIndex = 0; - bool prevWasSpace = false; - int lineCount = 0; - bool isLineStart = true; - - var calliString = new CalliString(calliBytes); + int accumulatedSpaces = 0; + int startOfLineGlyphIndex = 0; + bool prevWasSpace = false; + int lineCount = 0; + bool isLineStart = true; + ref FontBlob font = ref fontMaterialSet[0]; + + var calliString = new CalliString(calliBytes); var characterEnumerator = calliString.GetEnumerator(); while (characterEnumerator.MoveNext()) { @@ -46,7 +46,7 @@ internal static unsafe void CreateRenderGlyphs(ref DynamicBuffer re { textConfiguration.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 font, in baseConfiguration, ref textConfiguration, ref richTextAttributes)) + if (RichTextParser.ValidateHtmlTag(in calliString, ref characterEnumerator, ref font, in baseConfiguration, ref textConfiguration, ref richTextTagIdentifiers)) { // Continue to next character continue; @@ -67,12 +67,12 @@ internal static unsafe void CreateRenderGlyphs(ref DynamicBuffer re //Handle line break if (unicode.value == 10) //Line feed { - var glyphsLine = renderGlyphs.AsNativeArray().GetSubArray(startOfLineGlyphIndex, renderGlyphs.Length - startOfLineGlyphIndex); + var glyphsLine = renderGlyphs.AsNativeArray().GetSubArray(startOfLineGlyphIndex, renderGlyphs.Length - startOfLineGlyphIndex); var overrideMode = textConfiguration.m_lineJustification; if ((overrideMode) == HorizontalAlignmentOptions.Justified) { // Don't perform justified spacing for the last line in the paragraph. - overrideMode |= HorizontalAlignmentOptions.Left; + overrideMode = HorizontalAlignmentOptions.Left; } ApplyHorizontalAlignmentToGlyphs(ref glyphsLine, ref characterGlyphIndicesWithPreceedingSpacesInLine, @@ -81,44 +81,45 @@ internal static unsafe void CreateRenderGlyphs(ref DynamicBuffer re lineCount++; isLineStart = true; - cumulativeOffset.x = 0; + cumulativeOffset.x = 0; cumulativeOffset.y -= font.lineHeight * font.baseScale * baseConfiguration.fontSize; continue; } - if (font.characters.TryGetGlyph(math.asuint(unicode.value), out var glyphIndex)) + if (font.TryGetGlyphIndex(math.asuint(unicode.value), out var glyphIndex)) { - ref var glyphBlob = ref font.characters[glyphIndex]; - var renderGlyph = new RenderGlyph + ref var glyphBlob = ref font.characters[glyphIndex]; + var renderGlyph = new RenderGlyph { blPosition = GetBottomLeftPosition(ref font, ref glyphBlob, textConfiguration.m_currentFontSize, adjustmentOffset + cumulativeOffset, false, false), trPosition = GetTopRightPosition(ref font, ref glyphBlob, textConfiguration.m_currentFontSize, adjustmentOffset + cumulativeOffset, false, false), - blUVA = glyphBlob.bottomLeftUV, - trUVA = glyphBlob.topRightUV, - blUVB = glyphBlob.bottomLeftUV2, - tlUVB = glyphBlob.topLeftUV2, - trUVB = glyphBlob.topRightUV2, - brUVB = glyphBlob.bottomRightUV2, + blUVA = glyphBlob.bottomLeftUV, + trUVA = glyphBlob.topRightUV, + blUVB = glyphBlob.bottomLeftUV2, + tlUVB = glyphBlob.topLeftUV2, + trUVB = glyphBlob.topRightUV2, + brUVB = glyphBlob.bottomRightUV2, blColor = textConfiguration.m_htmlColor, tlColor = textConfiguration.m_htmlColor, trColor = textConfiguration.m_htmlColor, brColor = textConfiguration.m_htmlColor, unicode = glyphBlob.unicode, - scale = textConfiguration.m_currentFontSize, + scale = textConfiguration.m_currentFontSize, }; var baseScale = font.baseScale; renderGlyphs.Add(renderGlyph); + fontMaterialSet.WriteFontMaterialIndexForGlyph(0); mappingWriter.AddCharNoTags(characterCount - 1, true); mappingWriter.AddCharWithTags(characterEnumerator.CurrentCharIndex, true); mappingWriter.AddBytes(characterEnumerator.CurrentByteIndex, unicode.LengthInUtf8Bytes(), true); adjustmentOffset = float2.zero; - var xAdvanceAdjustment = 0f; - var yAdvanceAdjustment = 0f; + var xAdvanceAdjustment = 0f; + var yAdvanceAdjustment = 0f; var xPlacementAdjustment = 0f; var yPlacementAdjustment = 0f; @@ -132,8 +133,8 @@ internal static unsafe void CreateRenderGlyphs(ref DynamicBuffer re if (glyphAdjustment.secondAdjustment.glyphUnicode == math.asuint(peekChar.value)) { - xAdvanceAdjustment = glyphAdjustment.firstAdjustment.xAdvance * renderGlyph.scale * baseScale; - yAdvanceAdjustment = glyphAdjustment.firstAdjustment.yAdvance * renderGlyph.scale * baseScale; + xAdvanceAdjustment = glyphAdjustment.firstAdjustment.xAdvance * renderGlyph.scale * baseScale; + yAdvanceAdjustment = glyphAdjustment.firstAdjustment.yAdvance * renderGlyph.scale * baseScale; xPlacementAdjustment = glyphAdjustment.firstAdjustment.xPlacement * renderGlyph.scale * baseScale; yPlacementAdjustment = glyphAdjustment.firstAdjustment.yPlacement * renderGlyph.scale * baseScale; break; @@ -255,8 +256,8 @@ static float2 GetBottomLeftPosition(ref FontBlob font, ref GlyphBlob glyph, floa if (isItalics) { // Shift Top vertices forward by half (Shear Value * height of character) and Bottom vertices back by same amount. - float shear = font.italicsStyleSlant * 0.01f; - float midPoint = ((font.capLine - font.baseLine) / 2) * font.baseScale * scale; + float shear = font.italicsStyleSlant * 0.01f; + float midPoint = ((font.capLine - font.baseLine) / 2) * font.baseScale * scale; float2 bottomShear = new float2(shear * (((glyph.horizontalBearingY - glyph.height - font.materialPadding - midPoint)) * font.baseScale * scale), 0); bottomLeft += bottomShear; @@ -286,8 +287,8 @@ static float2 GetTopRightPosition(ref FontBlob font, ref GlyphBlob glyph, float if (isItalics) { // Shift Top vertices forward by half (Shear Value * height of character) and Bottom vertices back by same amount. - float shear = font.italicsStyleSlant * 0.01f; - float midPoint = ((font.capLine - font.baseLine) / 2) * font.baseScale * scale; + float shear = font.italicsStyleSlant * 0.01f; + float midPoint = ((font.capLine - font.baseLine) / 2) * font.baseScale * scale; float2 topShear = new float2(shear * ((glyph.horizontalBearingY + font.materialPadding - midPoint) * font.baseScale * scale), 0); topRight += topShear; @@ -297,7 +298,7 @@ static float2 GetTopRightPosition(ref FontBlob font, ref GlyphBlob glyph, float } static unsafe void ApplyHorizontalAlignmentToGlyphs(ref NativeArray glyphs, - ref FixedList512Bytes characterGlyphIndicesWithPreceedingSpacesInLine, + ref FixedList512Bytes characterGlyphIndicesWithPreceedingSpacesInLine, float width, HorizontalAlignmentOptions alignMode) { @@ -328,9 +329,9 @@ static unsafe void ApplyHorizontalAlignmentToGlyphs(ref NativeArray } else // Justified { - float nudgePerSpace = (width - glyphsPtr[glyphs.Length - 1].trPosition.x) / characterGlyphIndicesWithPreceedingSpacesInLine.Length; + float nudgePerSpace = (width - glyphsPtr[glyphs.Length - 1].trPosition.x) / characterGlyphIndicesWithPreceedingSpacesInLine.Length; float accumulatedOffset = 0f; - int indexInIndices = 0; + int indexInIndices = 0; for (int i = 0; i < glyphs.Length; i++) { while (indexInIndices < characterGlyphIndicesWithPreceedingSpacesInLine.Length && @@ -347,15 +348,19 @@ static unsafe void ApplyHorizontalAlignmentToGlyphs(ref NativeArray characterGlyphIndicesWithPreceedingSpacesInLine.Clear(); } - static unsafe void ApplyVerticalAlignmentToGlyphs(ref DynamicBuffer glyphs, int fullLineCount, VerticalAlignmentOptions alignMode, ref FontBlob font, float fontSize) + static unsafe void ApplyVerticalAlignmentToGlyphs(ref DynamicBuffer glyphs, + int fullLineCount, + VerticalAlignmentOptions alignMode, + ref FontBlob font, + float fontSize) { var glyphsPtr = (RenderGlyph*)glyphs.GetUnsafePtr(); if ((alignMode) == VerticalAlignmentOptions.Top) { // 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; + var offset = font.ascentLine - font.baseLine; + offset *= font.baseScale * fontSize; for (int i = 0; i < glyphs.Length; i++) { glyphsPtr[i].blPosition.y -= offset; @@ -365,8 +370,8 @@ static unsafe void ApplyVerticalAlignmentToGlyphs(ref DynamicBuffer 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; + 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++) { glyphsPtr[i].blPosition.y += offset; @@ -377,7 +382,7 @@ static unsafe void ApplyVerticalAlignmentToGlyphs(ref DynamicBuffer { // 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; + var offset = newlineSpace + (font.baseLine - font.descentLine) * font.baseScale * fontSize; for (int i = 0; i < glyphs.Length; i++) { glyphsPtr[i].blPosition.y += offset; diff --git a/Calligraphics/Internal/RichText/CalliExtensions.cs b/Calligraphics/Internal/RichText/CalliExtensions.cs index bf86dea..9550b63 100644 --- a/Calligraphics/Internal/RichText/CalliExtensions.cs +++ b/Calligraphics/Internal/RichText/CalliExtensions.cs @@ -1,9 +1,9 @@ using Unity.Collections; using UnityEngine; -namespace Latios.Calligraphics.Extensions +namespace Latios.Calligraphics { - public static class Calli_Ext + internal static class CalligraphicsInternalExtensions { public static void GetSubString(this in CalliString calliString, ref FixedString128Bytes htmlTag, int startIndex, int length) { diff --git a/Calligraphics/Internal/RichText/Calli_FontStyleStack.cs b/Calligraphics/Internal/RichText/FontStyleStack.cs similarity index 91% rename from Calligraphics/Internal/RichText/Calli_FontStyleStack.cs rename to Calligraphics/Internal/RichText/FontStyleStack.cs index 55eba2e..d18ded4 100644 --- a/Calligraphics/Internal/RichText/Calli_FontStyleStack.cs +++ b/Calligraphics/Internal/RichText/FontStyleStack.cs @@ -1,11 +1,11 @@ using UnityEngine.TextCore.Text; -namespace Latios.Calligraphics +namespace Latios.Calligraphics.RichText { /// /// Structure used to track basic XML tags which are binary (on / off) /// - public struct Calli_FontStyleStack + internal struct FontStyleStack { public byte bold; public byte italic; @@ -23,16 +23,16 @@ public struct Calli_FontStyleStack /// public void Clear() { - bold = 0; - italic = 0; - underline = 0; + bold = 0; + italic = 0; + underline = 0; strikethrough = 0; - highlight = 0; - superscript = 0; - subscript = 0; - uppercase = 0; - lowercase = 0; - smallcaps = 0; + highlight = 0; + superscript = 0; + subscript = 0; + uppercase = 0; + lowercase = 0; + smallcaps = 0; } public byte Add(FontStyles style) diff --git a/Calligraphics/Internal/RichText/Calli_FontStyleStack.cs.meta b/Calligraphics/Internal/RichText/FontStyleStack.cs.meta similarity index 100% rename from Calligraphics/Internal/RichText/Calli_FontStyleStack.cs.meta rename to Calligraphics/Internal/RichText/FontStyleStack.cs.meta diff --git a/Calligraphics/Internal/RichText/HighLightState.cs b/Calligraphics/Internal/RichText/HighLightState.cs index de26b00..12ac16e 100644 --- a/Calligraphics/Internal/RichText/HighLightState.cs +++ b/Calligraphics/Internal/RichText/HighLightState.cs @@ -1,16 +1,15 @@ -using Latios.Calligraphics.Extensions; using UnityEngine; -namespace Latios.Calligraphics +namespace Latios.Calligraphics.RichText { - public struct HighlightState + internal struct HighlightState { - public Color32 color; - public Calli_Offset padding; + public Color32 color; + public RectOffsets padding; - public HighlightState(Color32 color, Calli_Offset padding) + public HighlightState(Color32 color, RectOffsets padding) { - this.color = color; + this.color = color; this.padding = padding; } diff --git a/Calligraphics/Internal/RichText/CalliOffset.cs b/Calligraphics/Internal/RichText/RectOffsets.cs similarity index 62% rename from Calligraphics/Internal/RichText/CalliOffset.cs rename to Calligraphics/Internal/RichText/RectOffsets.cs index c131e12..868c87c 100644 --- a/Calligraphics/Internal/RichText/CalliOffset.cs +++ b/Calligraphics/Internal/RichText/RectOffsets.cs @@ -1,6 +1,6 @@ -namespace Latios.Calligraphics +namespace Latios.Calligraphics.RichText { - public struct Calli_Offset + internal struct RectOffsets { public float left { get { return m_Left; } set { m_Left = value; } } @@ -17,7 +17,7 @@ public struct Calli_Offset /// /// /// - public static Calli_Offset zero { get { return k_ZeroOffset; } } + public static RectOffsets zero { get { return k_ZeroOffset; } } // ============================================= // Private backing fields for public properties. @@ -28,7 +28,7 @@ public struct Calli_Offset float m_Top; float m_Bottom; - static readonly Calli_Offset k_ZeroOffset = new Calli_Offset(0F, 0F, 0F, 0F); + static readonly RectOffsets k_ZeroOffset = new RectOffsets(0F, 0F, 0F, 0F); /// /// @@ -37,11 +37,11 @@ public struct Calli_Offset /// /// /// - public Calli_Offset(float left, float right, float top, float bottom) + public RectOffsets(float left, float right, float top, float bottom) { - m_Left = left; - m_Right = right; - m_Top = top; + m_Left = left; + m_Right = right; + m_Top = top; m_Bottom = bottom; } @@ -50,30 +50,30 @@ public Calli_Offset(float left, float right, float top, float bottom) /// /// /// - public Calli_Offset(float horizontal, float vertical) + public RectOffsets(float horizontal, float vertical) { - m_Left = horizontal; - m_Right = horizontal; - m_Top = vertical; + m_Left = horizontal; + m_Right = horizontal; + m_Top = vertical; m_Bottom = vertical; } - public static bool operator ==(Calli_Offset lhs, Calli_Offset rhs) + public static bool operator ==(RectOffsets lhs, RectOffsets rhs) { return lhs.m_Left == rhs.m_Left && - lhs.m_Right == rhs.m_Right && - lhs.m_Top == rhs.m_Top && - lhs.m_Bottom == rhs.m_Bottom; + lhs.m_Right == rhs.m_Right && + lhs.m_Top == rhs.m_Top && + lhs.m_Bottom == rhs.m_Bottom; } - public static bool operator !=(Calli_Offset lhs, Calli_Offset rhs) + public static bool operator !=(RectOffsets lhs, RectOffsets rhs) { return !(lhs == rhs); } - public static Calli_Offset operator *(Calli_Offset a, float b) + public static RectOffsets operator *(RectOffsets a, float b) { - return new Calli_Offset(a.m_Left * b, a.m_Right * b, a.m_Top * b, a.m_Bottom * b); + return new RectOffsets(a.m_Left * b, a.m_Right * b, a.m_Top * b, a.m_Bottom * b); } public override int GetHashCode() @@ -86,7 +86,7 @@ public override bool Equals(object obj) return base.Equals(obj); } - public bool Equals(Calli_Offset other) + public bool Equals(RectOffsets other) { return base.Equals(other); } diff --git a/Calligraphics/Internal/RichText/CalliOffset.cs.meta b/Calligraphics/Internal/RichText/RectOffsets.cs.meta similarity index 100% rename from Calligraphics/Internal/RichText/CalliOffset.cs.meta rename to Calligraphics/Internal/RichText/RectOffsets.cs.meta diff --git a/Calligraphics/Internal/RichText/RichTextAttribute.cs b/Calligraphics/Internal/RichText/RichTextAttribute.cs deleted file mode 100644 index bfe72ee..0000000 --- a/Calligraphics/Internal/RichText/RichTextAttribute.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Latios.Calligraphics.RichText -{ - public struct RichTextAttribute - { - public RichTextTagType tagType; - public int nameHashCode; - public int valueHashCode; - public TagValueType valueType; - public int valueStartIndex; //bytes position, not char! - public int valueLength; //byte length, not char! - public TagUnitType unitType; - public static RichTextAttribute Empty => new RichTextAttribute - { - tagType = RichTextTagType.INVALID, - nameHashCode = 0, - valueHashCode = 0, - valueStartIndex = 0, - valueLength = 0, - valueType = TagValueType.None, - unitType = TagUnitType.Pixels, - }; - } -} - diff --git a/Calligraphics/Internal/RichText/RichTextParser.cs b/Calligraphics/Internal/RichText/RichTextParser.cs index fc74467..c53239d 100644 --- a/Calligraphics/Internal/RichText/RichTextParser.cs +++ b/Calligraphics/Internal/RichText/RichTextParser.cs @@ -1,9 +1,8 @@ -using Latios.Calligraphics.Extensions; using Unity.Collections; using UnityEngine; using UnityEngine.TextCore.Text; -namespace Latios.Calligraphics.RichText.Parsing +namespace Latios.Calligraphics.RichText { internal static class RichTextParser { @@ -17,72 +16,72 @@ internal static class RichTextParser internal static bool ValidateHtmlTag( in CalliString calliString, ref CalliString.Enumerator enumerator, - ref FontBlob currenFont,//will need to be replaced with List of FontAssets to allow switching + ref FontBlob currenFont, //will need to be replaced with List of FontAssets to allow switching in TextBaseConfiguration baseConfiguration, ref TextConfiguration textConfiguration, - ref FixedList512Bytes richTextAttributes) //this is just a cache to avoid allocation + ref FixedList512Bytes richTextTagIndentifiers) //this is just a cache to avoid allocation { - richTextAttributes.Clear(); - int tagCharCount = 0; - int tagByteCount = 0; - int startByteIndex = enumerator.CurrentByteIndex; - ParserState attributeFlag = ParserState.Zero; - - int attributeIndex = richTextAttributes.Length; - richTextAttributes.Add(RichTextAttribute.Empty); - ref var currentAttribute = ref richTextAttributes.ElementAt(attributeIndex); - TagValueType tagValueType = currentAttribute.valueType = TagValueType.None; - TagUnitType tagUnitType = currentAttribute.unitType = TagUnitType.Pixels; - - bool isTagSet = false; + richTextTagIndentifiers.Clear(); + int tagCharCount = 0; + int tagByteCount = 0; + int startByteIndex = enumerator.CurrentByteIndex; + ParserState tagIndentifierFlag = ParserState.Zero; + + int tagIndentifierIndex = richTextTagIndentifiers.Length; + richTextTagIndentifiers.Add(RichTextTagIdentifier.Empty); + ref var currentTagIndentifier = ref richTextTagIndentifiers.ElementAt(tagIndentifierIndex); + TagValueType tagValueType = currentTagIndentifier.valueType = TagValueType.None; + TagUnitType tagUnitType = currentTagIndentifier.unitType = TagUnitType.Pixels; + + bool isTagSet = false; bool isValidHtmlTag = false; - Unicode.Rune unicode = Unicode.BadRune; - int charCount = 0; + Unicode.Rune unicode = Unicode.BadRune; + int charCount = 0; while (enumerator.MoveNext() && (unicode = enumerator.Current) != Unicode.BadRune && unicode != '<') { - if (unicode == '>') // ASCII Code of End HTML tag '>' + if (unicode == '>') // ASCII Code of End HTML tag '>' { isValidHtmlTag = true; break; } - int byteCount = unicode.LengthInUtf8Bytes(); - tagCharCount += 1; - tagByteCount += byteCount; + int byteCount = unicode.LengthInUtf8Bytes(); + tagCharCount += 1; + tagByteCount += byteCount; - if (attributeFlag == ParserState.One) + if (tagIndentifierFlag == ParserState.One) { if (tagValueType == TagValueType.None) { - // Check for attribute type + // Check for tagIndentifier type if (unicode == '+' || unicode == '-' || unicode == '.' || Unicode.Rune.IsDigit(unicode)) { - tagUnitType = TagUnitType.Pixels; - tagValueType = currentAttribute.valueType = TagValueType.NumericalValue; - currentAttribute.valueStartIndex = enumerator.CurrentByteIndex - unicode.LengthInUtf8Bytes(); - currentAttribute.valueLength += byteCount; + tagUnitType = TagUnitType.Pixels; + tagValueType = currentTagIndentifier.valueType = TagValueType.NumericalValue; + currentTagIndentifier.valueStartIndex = enumerator.CurrentByteIndex - unicode.LengthInUtf8Bytes(); + currentTagIndentifier.valueLength += byteCount; } else if (unicode == '#') { - tagUnitType = TagUnitType.Pixels; - tagValueType = currentAttribute.valueType = TagValueType.ColorValue; - currentAttribute.valueStartIndex = enumerator.CurrentByteIndex - unicode.LengthInUtf8Bytes(); - currentAttribute.valueLength += byteCount; + tagUnitType = TagUnitType.Pixels; + tagValueType = currentTagIndentifier.valueType = TagValueType.ColorValue; + currentTagIndentifier.valueStartIndex = enumerator.CurrentByteIndex - unicode.LengthInUtf8Bytes(); + currentTagIndentifier.valueLength += byteCount; } else if (unicode == '"') { - tagUnitType = TagUnitType.Pixels; - tagValueType = currentAttribute.valueType = TagValueType.StringValue; - currentAttribute.valueStartIndex = enumerator.CurrentByteIndex; + tagUnitType = TagUnitType.Pixels; + tagValueType = currentTagIndentifier.valueType = TagValueType.StringValue; + currentTagIndentifier.valueStartIndex = enumerator.CurrentByteIndex; } else { - tagUnitType = TagUnitType.Pixels; - tagValueType = currentAttribute.valueType = TagValueType.StringValue; - currentAttribute.valueStartIndex = enumerator.CurrentByteIndex - unicode.LengthInUtf8Bytes(); - currentAttribute.valueHashCode = (currentAttribute.valueHashCode << 5) + currentAttribute.valueHashCode ^ unicode.value; - currentAttribute.valueLength += byteCount; + tagUnitType = TagUnitType.Pixels; + tagValueType = currentTagIndentifier.valueType = TagValueType.StringValue; + currentTagIndentifier.valueStartIndex = enumerator.CurrentByteIndex - unicode.LengthInUtf8Bytes(); + currentTagIndentifier.valueHashCode = (currentTagIndentifier.valueHashCode << 5) + currentTagIndentifier.valueHashCode ^ unicode.value; + currentTagIndentifier.valueLength += byteCount; } } else @@ -92,45 +91,45 @@ internal static bool ValidateHtmlTag( // Check for termination of numerical value. if (unicode == 'p' || unicode == 'e' || unicode == '%' || unicode == ' ') { - attributeFlag = ParserState.Two; - tagValueType = TagValueType.None; + tagIndentifierFlag = ParserState.Two; + tagValueType = TagValueType.None; switch (unicode.value) { case 'e': - currentAttribute.unitType = tagUnitType = TagUnitType.FontUnits; + currentTagIndentifier.unitType = tagUnitType = TagUnitType.FontUnits; break; case '%': - currentAttribute.unitType = tagUnitType = TagUnitType.Percentage; + currentTagIndentifier.unitType = tagUnitType = TagUnitType.Percentage; break; default: - currentAttribute.unitType = tagUnitType = TagUnitType.Pixels; + currentTagIndentifier.unitType = tagUnitType = TagUnitType.Pixels; break; } - attributeIndex += 1; - richTextAttributes.Add(RichTextAttribute.Empty); - currentAttribute = ref richTextAttributes.ElementAt(attributeIndex); + tagIndentifierIndex += 1; + richTextTagIndentifiers.Add(RichTextTagIdentifier.Empty); + currentTagIndentifier = ref richTextTagIndentifiers.ElementAt(tagIndentifierIndex); } - else if (attributeFlag != ParserState.Two) + else if (tagIndentifierFlag != ParserState.Two) { - currentAttribute.valueLength += byteCount; + currentTagIndentifier.valueLength += byteCount; } } else if (tagValueType == TagValueType.ColorValue) { if (unicode != ' ') { - currentAttribute.valueLength += byteCount; + currentTagIndentifier.valueLength += byteCount; } else { - attributeFlag = ParserState.Two; - tagValueType = TagValueType.None; - tagUnitType = TagUnitType.Pixels; - attributeIndex += 1; - richTextAttributes.Add(RichTextAttribute.Empty); - currentAttribute = ref richTextAttributes.ElementAt(attributeIndex); + tagIndentifierFlag = ParserState.Two; + tagValueType = TagValueType.None; + tagUnitType = TagUnitType.Pixels; + tagIndentifierIndex += 1; + richTextTagIndentifiers.Add(RichTextTagIdentifier.Empty); + currentTagIndentifier = ref richTextTagIndentifiers.ElementAt(tagIndentifierIndex); } } else if (tagValueType == TagValueType.StringValue) @@ -138,46 +137,46 @@ internal static bool ValidateHtmlTag( // Compute HashCode value for the named tag. if (unicode != '"') { - currentAttribute.valueHashCode = (currentAttribute.valueHashCode << 5) + currentAttribute.valueHashCode ^ unicode.value; - currentAttribute.valueLength += byteCount; + currentTagIndentifier.valueHashCode = (currentTagIndentifier.valueHashCode << 5) + currentTagIndentifier.valueHashCode ^ unicode.value; + currentTagIndentifier.valueLength += byteCount; } else { - attributeFlag = ParserState.Two; - tagValueType = TagValueType.None; - tagUnitType = TagUnitType.Pixels; - attributeIndex += 1; - richTextAttributes.Add(RichTextAttribute.Empty); - currentAttribute = ref richTextAttributes.ElementAt(attributeIndex); + tagIndentifierFlag = ParserState.Two; + tagValueType = TagValueType.None; + tagUnitType = TagUnitType.Pixels; + tagIndentifierIndex += 1; + richTextTagIndentifiers.Add(RichTextTagIdentifier.Empty); + currentTagIndentifier = ref richTextTagIndentifiers.ElementAt(tagIndentifierIndex); } } } } - if (unicode == '=') // '=' - attributeFlag = ParserState.One; + tagIndentifierFlag = ParserState.One; - // Compute HashCode for the name of the attribute - if (attributeFlag == ParserState.Zero && unicode == ' ') + // Compute HashCode for the name of the tagIndentifier + if (tagIndentifierFlag == ParserState.Zero && unicode == ' ') { - if (isTagSet) return false; + if (isTagSet) + return false; - isTagSet = true; - attributeFlag = ParserState.Two; + isTagSet = true; + tagIndentifierFlag = ParserState.Two; - tagValueType = TagValueType.None; - tagUnitType = TagUnitType.Pixels; - attributeIndex += 1; - richTextAttributes.Add(RichTextAttribute.Empty); - currentAttribute = ref richTextAttributes.ElementAt(attributeIndex); + tagValueType = TagValueType.None; + tagUnitType = TagUnitType.Pixels; + tagIndentifierIndex += 1; + richTextTagIndentifiers.Add(RichTextTagIdentifier.Empty); + currentTagIndentifier = ref richTextTagIndentifiers.ElementAt(tagIndentifierIndex); } - if (attributeFlag == ParserState.Zero) - currentAttribute.nameHashCode = (currentAttribute.nameHashCode << 3) - currentAttribute.nameHashCode + unicode.value; + if (tagIndentifierFlag == ParserState.Zero) + currentTagIndentifier.nameHashCode = (currentTagIndentifier.nameHashCode << 3) - currentTagIndentifier.nameHashCode + unicode.value; - if (attributeFlag == ParserState.Two && unicode == ' ') - attributeFlag = ParserState.Zero; + if (tagIndentifierFlag == ParserState.Two && unicode == ' ') + tagIndentifierFlag = ParserState.Zero; } if (!isValidHtmlTag) @@ -185,15 +184,15 @@ internal static bool ValidateHtmlTag( return false; } - ref var firstAttribute = ref richTextAttributes.ElementAt(0); + ref var firstTagIndentifier = ref richTextTagIndentifiers.ElementAt(0); calliString.GetSubString(ref textConfiguration.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 && (firstAttribute.nameHashCode != 53822163 && firstAttribute.nameHashCode != 49429939)) + if (textConfiguration.tag_NoParsing && (firstTagIndentifier.nameHashCode != 53822163 && firstTagIndentifier.nameHashCode != 49429939)) return false; - else if (firstAttribute.nameHashCode == 53822163 || firstAttribute.nameHashCode == 49429939) + else if (firstTagIndentifier.nameHashCode == 53822163 || firstTagIndentifier.nameHashCode == 49429939) { textConfiguration.tag_NoParsing = false; return true; @@ -202,11 +201,11 @@ internal static bool ValidateHtmlTag( // Color tag just starting with hex (no assignment) if (textConfiguration.m_htmlTag[0] == 35) { - firstAttribute.tagType = RichTextTagType.Color; + firstTagIndentifier.tagType = RichTextTagType.Color; // tagCharCount == 4: Color <#FFF> 3 Hex values (short form) // tagCharCount == 5: Color <#FFF7> 4 Hex values with alpha (short form) - // tagCharCount == 7: Color <#FF00FF> - // tagCharCount == 9: Color <#FF00FF00> with alpha + // tagCharCount == 7: Color <#FF00FF> + // tagCharCount == 9: Color <#FF00FF00> with alpha if (tagCharCount == 4 || tagCharCount == 5 || tagCharCount == 7 || tagCharCount == 9) { textConfiguration.m_htmlColor = HexCharsToColor(textConfiguration.m_htmlTag, tagCharCount); @@ -219,72 +218,76 @@ internal static bool ValidateHtmlTag( float value = 0; float fontScale; - switch (firstAttribute.nameHashCode) + switch (firstTagIndentifier.nameHashCode) { - case 98: // - case 66: // - textConfiguration.m_FontStyleInternal |= FontStyles.Bold; + case 98: // + case 66: // + textConfiguration.m_fontStyleInternal |= FontStyles.Bold; textConfiguration.m_fontStyleStack.Add(FontStyles.Bold); - textConfiguration.m_FontWeightInternal = FontWeight.Bold; + textConfiguration.m_fontWeightInternal = FontWeight.Bold; return true; - case 427: // - case 395: // + case 427: // + case 395: // if ((baseConfiguration.fontStyle & FontStyles.Bold) != FontStyles.Bold) { if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Bold) == 0) { - textConfiguration.m_FontStyleInternal &= ~FontStyles.Bold; - textConfiguration.m_FontWeightInternal = textConfiguration.m_FontWeightStack.Peek(); + textConfiguration.m_fontStyleInternal &= ~FontStyles.Bold; + textConfiguration.m_fontWeightInternal = textConfiguration.m_fontWeightStack.Peek(); } } return true; - case 105: // - case 73: // - firstAttribute.tagType = RichTextTagType.Italic; - textConfiguration.m_FontStyleInternal |= FontStyles.Italic; + case 105: // + case 73: // + firstTagIndentifier.tagType = RichTextTagType.Italic; + textConfiguration.m_fontStyleInternal |= FontStyles.Italic; textConfiguration.m_fontStyleStack.Add(FontStyles.Italic); - if (richTextAttributes.Length > 0 && richTextAttributes[1].nameHashCode == 276531 || richTextAttributes[1].nameHashCode == 186899) + if (richTextTagIndentifiers.Length > 0 && richTextTagIndentifiers[1].nameHashCode == 276531 || richTextTagIndentifiers[1].nameHashCode == 186899) { - // Reject tag if value is invalid. - calliString.GetSubString(ref textConfiguration.m_htmlTag, richTextAttributes[1].valueStartIndex, richTextAttributes[1].valueLength); + calliString.GetSubString(ref textConfiguration.m_htmlTag, richTextTagIndentifiers[1].valueStartIndex, richTextTagIndentifiers[1].valueLength); if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; - textConfiguration.m_ItalicAngle = (short)value; + textConfiguration.m_italicAngle = (short)value; // Make sure angle is within valid range. - if (textConfiguration.m_ItalicAngle < -180 || textConfiguration.m_ItalicAngle > 180) return false; + if (textConfiguration.m_italicAngle < -180 || textConfiguration.m_italicAngle > 180) + return false; } else - textConfiguration.m_ItalicAngle = currenFont.italicsStyleSlant; + textConfiguration.m_italicAngle = currenFont.italicsStyleSlant; - textConfiguration.m_ItalicAngleStack.Add(textConfiguration.m_ItalicAngle); + textConfiguration.m_italicAngleStack.Add(textConfiguration.m_italicAngle); return true; - case 434: // - case 402: // + case 434: // + case 402: // if ((baseConfiguration.fontStyle & FontStyles.Italic) != FontStyles.Italic) { - textConfiguration.m_ItalicAngle = textConfiguration.m_ItalicAngleStack.RemoveExceptRoot(); + textConfiguration.m_italicAngle = textConfiguration.m_italicAngleStack.RemoveExceptRoot(); if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Italic) == 0) - textConfiguration.m_FontStyleInternal &= ~FontStyles.Italic; + textConfiguration.m_fontStyleInternal &= ~FontStyles.Italic; } return true; - case 115: // - case 83: // - textConfiguration.m_FontStyleInternal |= FontStyles.Strikethrough; + case 115: // + case 83: // + textConfiguration.m_fontStyleInternal |= FontStyles.Strikethrough; textConfiguration.m_fontStyleStack.Add(FontStyles.Strikethrough); - if (richTextAttributes.Length > 0 && richTextAttributes[1].nameHashCode == 281955 || richTextAttributes[1].nameHashCode == 192323) + if (richTextTagIndentifiers.Length > 0 && richTextTagIndentifiers[1].nameHashCode == 281955 || richTextTagIndentifiers[1].nameHashCode == 192323) { - calliString.GetSubString(ref textConfiguration.m_htmlTag, richTextAttributes[1].valueStartIndex, richTextAttributes[1].valueLength); - charCount = richTextAttributes[1].valueLength - richTextAttributes[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 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); } else textConfiguration.m_strikethroughColor = textConfiguration.m_htmlColor; @@ -292,26 +295,28 @@ internal static bool ValidateHtmlTag( textConfiguration.m_strikethroughColorStack.Add(textConfiguration.m_strikethroughColor); return true; - case 444: // - case 412: // + case 444: // + case 412: // if ((baseConfiguration.fontStyle & FontStyles.Strikethrough) != FontStyles.Strikethrough) { if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Strikethrough) == 0) - textConfiguration.m_FontStyleInternal &= ~FontStyles.Strikethrough; + textConfiguration.m_fontStyleInternal &= ~FontStyles.Strikethrough; } textConfiguration.m_strikethroughColor = textConfiguration.m_strikethroughColorStack.RemoveExceptRoot(); return true; - case 117: // - case 85: // - textConfiguration.m_FontStyleInternal |= FontStyles.Underline; + case 117: // + case 85: // + textConfiguration.m_fontStyleInternal |= FontStyles.Underline; textConfiguration.m_fontStyleStack.Add(FontStyles.Underline); - if (richTextAttributes.Length > 0 && richTextAttributes[1].nameHashCode == 281955 || richTextAttributes[1].nameHashCode == 192323) + if (richTextTagIndentifiers.Length > 0 && richTextTagIndentifiers[1].nameHashCode == 281955 || richTextTagIndentifiers[1].nameHashCode == 192323) { - calliString.GetSubString(ref textConfiguration.m_htmlTag, richTextAttributes[1].valueStartIndex, richTextAttributes[1].valueLength); - charCount = richTextAttributes[1].valueLength - richTextAttributes[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 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); } else textConfiguration.m_underlineColor = textConfiguration.m_htmlColor; @@ -319,58 +324,58 @@ internal static bool ValidateHtmlTag( textConfiguration.m_underlineColorStack.Add(textConfiguration.m_underlineColor); return true; - case 446: // - case 414: // + case 446: // + case 414: // if ((baseConfiguration.fontStyle & FontStyles.Underline) != FontStyles.Underline) { textConfiguration.m_underlineColor = textConfiguration.m_underlineColorStack.RemoveExceptRoot(); if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Underline) == 0) - textConfiguration.m_FontStyleInternal &= ~FontStyles.Underline; + textConfiguration.m_fontStyleInternal &= ~FontStyles.Underline; } textConfiguration.m_underlineColor = textConfiguration.m_underlineColorStack.RemoveExceptRoot(); return true; - case 43045: // - case 30245: // - textConfiguration.m_FontStyleInternal |= FontStyles.Highlight; + case 43045: // + case 30245: // + textConfiguration.m_fontStyleInternal |= FontStyles.Highlight; textConfiguration.m_fontStyleStack.Add(FontStyles.Highlight); - Color32 highlightColor = new Color32(255, 255, 0, 64); - Calli_Offset highlightPadding = Calli_Offset.zero; + Color32 highlightColor = new Color32(255, 255, 0, 64); + RectOffsets highlightPadding = RectOffsets.zero; - // Handle Mark Tag and potential attributes - for (int i = 0; i < richTextAttributes.Length && richTextAttributes[i].nameHashCode != 0; i++) + // Handle Mark Tag and potential tagIndentifiers + for (int i = 0; i < richTextTagIndentifiers.Length && richTextTagIndentifiers[i].nameHashCode != 0; i++) { - int nameHashCode = richTextAttributes[i].nameHashCode; + int nameHashCode = richTextTagIndentifiers[i].nameHashCode; switch (nameHashCode) { // Mark tag case 43045: case 30245: - if (richTextAttributes[i].valueType == TagValueType.ColorValue) + if (richTextTagIndentifiers[i].valueType == TagValueType.ColorValue) { - //is this a bug in TMP Pro? -->should be richTextAttributes[i] and not firstAttribute - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); - charCount = firstAttribute.valueLength - firstAttribute.valueStartIndex; + //is this a bug in TMP Pro? -->should be richTextTagIndentifiers[i] and not firstTagIndentifier + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + charCount = firstTagIndentifier.valueLength - firstTagIndentifier.valueStartIndex; highlightColor = HexCharsToColor(textConfiguration.m_htmlTag, charCount); } break; - // Color attribute + // Color tagIndentifier case 281955: - calliString.GetSubString(ref textConfiguration.m_htmlTag, richTextAttributes[i].valueStartIndex, richTextAttributes[i].valueLength); - charCount = richTextAttributes[i].valueLength - richTextAttributes[i].valueStartIndex; + calliString.GetSubString(ref textConfiguration.m_htmlTag, richTextTagIndentifiers[i].valueStartIndex, richTextTagIndentifiers[i].valueLength); + charCount = richTextTagIndentifiers[i].valueLength - richTextTagIndentifiers[i].valueStartIndex; highlightColor = HexCharsToColor(textConfiguration.m_htmlTag, charCount); break; - // Padding attribute + // Padding tagIndentifier case 15087385: - //int paramCount = GetAttributeParameters(calliString, richTextAttributes[i].valueStartIndex, richTextAttributes[i].valueLength, ref m_attributeParameterValues); + //int paramCount = GetTagIndentifierParameters(calliString, richTextTagIndentifiers[i].valueStartIndex, richTextTagIndentifiers[i].valueLength, ref m_tagIndentifierParameterValues); //if (paramCount != 4) return false; - //highlightPadding = new Calli_Offset(m_attributeParameterValues[0], m_attributeParameterValues[1], m_attributeParameterValues[2], m_attributeParameterValues[3]); + //highlightPadding = new Calli_Offset(m_tagIndentifierParameterValues[0], m_tagIndentifierParameterValues[1], m_tagIndentifierParameterValues[2], m_tagIndentifierParameterValues[3]); //highlightPadding *= baseConfiguration.fontSize * 0.01f * (richtextAdjustments.m_isOrthographic ? 1 : 0.1f); break; } @@ -379,71 +384,73 @@ internal static bool ValidateHtmlTag( highlightColor.a = textConfiguration.m_htmlColor.a < highlightColor.a ? (byte)(textConfiguration.m_htmlColor.a) : (byte)(highlightColor.a); HighlightState state = new HighlightState(highlightColor, highlightPadding); - textConfiguration.m_HighlightStateStack.Add(state); + textConfiguration.m_highlightStateStack.Add(state); return true; - case 155892: // - case 143092: // + case 155892: // + case 143092: // if ((baseConfiguration.fontStyle & FontStyles.Highlight) != FontStyles.Highlight) { - textConfiguration.m_HighlightStateStack.RemoveExceptRoot(); + textConfiguration.m_highlightStateStack.RemoveExceptRoot(); if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Highlight) == 0) - textConfiguration.m_FontStyleInternal &= ~FontStyles.Highlight; + textConfiguration.m_fontStyleInternal &= ~FontStyles.Highlight; } return true; - case 6552: // - case 4728: // + case 6552: // + case 4728: // textConfiguration.m_fontScaleMultiplier *= currenFont.subscriptSize > 0 ? currenFont.subscriptSize : 1; textConfiguration.m_baselineOffsetStack.Add(textConfiguration.m_baselineOffset); - fontScale = (textConfiguration.m_currentFontSize / currenFont.pointSize * currenFont.scale * (baseConfiguration.isOrthographic ? 1 : 0.1f)); + fontScale = + (textConfiguration.m_currentFontSize / currenFont.pointSize * currenFont.scale * (baseConfiguration.isOrthographic ? 1 : 0.1f)); textConfiguration.m_baselineOffset += currenFont.subscriptOffset * fontScale * textConfiguration.m_fontScaleMultiplier; textConfiguration.m_fontStyleStack.Add(FontStyles.Subscript); - textConfiguration.m_FontStyleInternal |= FontStyles.Subscript; + textConfiguration.m_fontStyleInternal |= FontStyles.Subscript; return true; - case 22673: // - case 20849: // - if ((textConfiguration.m_FontStyleInternal & FontStyles.Subscript) == FontStyles.Subscript) + case 22673: // + case 20849: // + if ((textConfiguration.m_fontStyleInternal & FontStyles.Subscript) == FontStyles.Subscript) { if (textConfiguration.m_fontScaleMultiplier < 1) { - textConfiguration.m_baselineOffset = textConfiguration.m_baselineOffsetStack.Pop(); + textConfiguration.m_baselineOffset = textConfiguration.m_baselineOffsetStack.Pop(); textConfiguration.m_fontScaleMultiplier /= currenFont.subscriptSize > 0 ? currenFont.subscriptSize : 1; } if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Subscript) == 0) - textConfiguration.m_FontStyleInternal &= ~FontStyles.Subscript; + textConfiguration.m_fontStyleInternal &= ~FontStyles.Subscript; } return true; - case 6566: // - case 4742: // + case 6566: // + case 4742: // textConfiguration.m_fontScaleMultiplier *= currenFont.superscriptSize > 0 ? currenFont.superscriptSize : 1; textConfiguration.m_baselineOffsetStack.Add(textConfiguration.m_baselineOffset); - fontScale = (textConfiguration.m_currentFontSize / currenFont.pointSize * currenFont.scale * (baseConfiguration.isOrthographic ? 1 : 0.1f)); + fontScale = + (textConfiguration.m_currentFontSize / currenFont.pointSize * currenFont.scale * (baseConfiguration.isOrthographic ? 1 : 0.1f)); textConfiguration.m_baselineOffset += currenFont.superscriptOffset * fontScale * textConfiguration.m_fontScaleMultiplier; textConfiguration.m_fontStyleStack.Add(FontStyles.Superscript); - textConfiguration.m_FontStyleInternal |= FontStyles.Superscript; + textConfiguration.m_fontStyleInternal |= FontStyles.Superscript; return true; - case 22687: // - case 20863: // - if ((textConfiguration.m_FontStyleInternal & FontStyles.Superscript) == FontStyles.Superscript) + case 22687: // + case 20863: // + if ((textConfiguration.m_fontStyleInternal & FontStyles.Superscript) == FontStyles.Superscript) { if (textConfiguration.m_fontScaleMultiplier < 1) { - textConfiguration.m_baselineOffset = textConfiguration.m_baselineOffsetStack.Pop(); + textConfiguration.m_baselineOffset = textConfiguration.m_baselineOffsetStack.Pop(); textConfiguration.m_fontScaleMultiplier /= currenFont.superscriptSize > 0 ? currenFont.superscriptSize : 1; } if (textConfiguration.m_fontStyleStack.Remove(FontStyles.Superscript) == 0) - textConfiguration.m_FontStyleInternal &= ~FontStyles.Superscript; + textConfiguration.m_fontStyleInternal &= ~FontStyles.Superscript; } return true; - case -330774850: // - case 2012149182: // - firstAttribute.tagType = RichTextTagType.FontWeight; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case -330774850: // + case 2012149182: // + firstTagIndentifier.tagType = RichTextTagType.FontWeight; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -451,51 +458,51 @@ internal static bool ValidateHtmlTag( switch ((int)value) { case 100: - textConfiguration.m_FontWeightInternal = FontWeight.Thin; + textConfiguration.m_fontWeightInternal = FontWeight.Thin; break; case 200: - textConfiguration.m_FontWeightInternal = FontWeight.ExtraLight; + textConfiguration.m_fontWeightInternal = FontWeight.ExtraLight; break; case 300: - textConfiguration.m_FontWeightInternal = FontWeight.Light; + textConfiguration.m_fontWeightInternal = FontWeight.Light; break; case 400: - textConfiguration.m_FontWeightInternal = FontWeight.Regular; + textConfiguration.m_fontWeightInternal = FontWeight.Regular; break; case 500: - textConfiguration.m_FontWeightInternal = FontWeight.Medium; + textConfiguration.m_fontWeightInternal = FontWeight.Medium; break; case 600: - textConfiguration.m_FontWeightInternal = FontWeight.SemiBold; + textConfiguration.m_fontWeightInternal = FontWeight.SemiBold; break; case 700: - textConfiguration.m_FontWeightInternal = FontWeight.Bold; + textConfiguration.m_fontWeightInternal = FontWeight.Bold; break; case 800: - textConfiguration.m_FontWeightInternal = FontWeight.Heavy; + textConfiguration.m_fontWeightInternal = FontWeight.Heavy; break; case 900: - textConfiguration.m_FontWeightInternal = FontWeight.Black; + textConfiguration.m_fontWeightInternal = FontWeight.Black; break; } - textConfiguration.m_FontWeightStack.Add(textConfiguration.m_FontWeightInternal); + textConfiguration.m_fontWeightStack.Add(textConfiguration.m_fontWeightInternal); return true; - case -1885698441: // - case 457225591: // - textConfiguration.m_FontWeightStack.RemoveExceptRoot(); + case -1885698441: // + case 457225591: // + textConfiguration.m_fontWeightStack.RemoveExceptRoot(); - if (textConfiguration.m_FontStyleInternal == FontStyles.Bold) - textConfiguration.m_FontWeightInternal = FontWeight.Bold; + if (textConfiguration.m_fontStyleInternal == FontStyles.Bold) + textConfiguration.m_fontWeightInternal = FontWeight.Bold; else - textConfiguration.m_FontWeightInternal = textConfiguration.m_FontWeightStack.Peek(); + textConfiguration.m_fontWeightInternal = textConfiguration.m_fontWeightStack.Peek(); return true; - case 6380: // - case 4556: // - firstAttribute.tagType = RichTextTagType.Position; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case 6380: // + case 4556: // + firstTagIndentifier.tagType = RichTextTagType.Position; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -513,14 +520,14 @@ internal static bool ValidateHtmlTag( return true; } return false; - case 22501: // - case 20677: // + case 22501: // + case 20677: // return true; - case 16034505: // - case 11642281: // - firstAttribute.tagType = RichTextTagType.VerticalOffset; + case 16034505: // + case 11642281: // + firstTagIndentifier.tagType = RichTextTagType.VerticalOffset; // Reject tag if value is invalid. - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -537,9 +544,9 @@ internal static bool ValidateHtmlTag( return false; } return false; - case 54741026: // - case 50348802: // - firstAttribute.tagType = RichTextTagType.VerticalOffset; + case 54741026: // + case 50348802: // + firstTagIndentifier.tagType = RichTextTagType.VerticalOffset; textConfiguration.m_baselineOffset = 0; return true; //case 43991: // @@ -558,18 +565,18 @@ internal static bool ValidateHtmlTag( ////case 800: //
//// m_forceLineBreak = true; //// return true; - case 43969: // - case 31169: // + case 43969: // + case 31169: // textConfiguration.m_isNonBreakingSpace = true; return true; - case 156816: // - case 144016: // + case 156816: // + case 144016: // textConfiguration.m_isNonBreakingSpace = false; return true; - case 45545: // - case 32745: // - firstAttribute.tagType = RichTextTagType.Size; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case 45545: // + case 32745: // + firstTagIndentifier.tagType = RichTextTagType.Size; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -577,19 +584,19 @@ internal static bool ValidateHtmlTag( switch (tagUnitType) { case TagUnitType.Pixels: - if (calliString[5] == 43) // + if (calliString[5] == 43) // { textConfiguration.m_currentFontSize = baseConfiguration.fontSize + value; textConfiguration.m_sizeStack.Add(textConfiguration.m_currentFontSize); return true; } - else if (calliString[5] == 45) // + else if (calliString[5] == 45) // { textConfiguration.m_currentFontSize = baseConfiguration.fontSize + value; textConfiguration.m_sizeStack.Add(textConfiguration.m_currentFontSize); return true; } - else // + else // { textConfiguration.m_currentFontSize = value; textConfiguration.m_sizeStack.Add(textConfiguration.m_currentFontSize); @@ -605,15 +612,15 @@ internal static bool ValidateHtmlTag( return true; } return false; - case 158392: // - case 145592: // + case 158392: // + case 145592: // textConfiguration.m_currentFontSize = textConfiguration.m_sizeStack.RemoveExceptRoot(); return true; //case 41311: // //case 28511: // - // int fontHashCode = firstAttribute.valueHashCode; - // int materialAttributeHashCode = richTextAttributes[1].nameHashCode; - // int materialHashCode = richTextAttributes[1].valueHashCode; + // int fontHashCode = firstTagIndentifier.valueHashCode; + // int materialTagIndentifierHashCode = richTextTagIndentifiers[1].nameHashCode; + // int materialHashCode = richTextTagIndentifiers[1].valueHashCode; // // Special handling for or // if (fontHashCode == 764638571 || fontHashCode == 523367755) @@ -641,12 +648,12 @@ internal static bool ValidateHtmlTag( // if (tempFont == null) // { // // Check for anyone registered to this callback - // tempFont = OnFontAssetRequest?.Invoke(fontHashCode, new string(calliString, firstAttribute.valueStartIndex, firstAttribute.valueLength)); + // tempFont = OnFontAssetRequest?.Invoke(fontHashCode, new string(calliString, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength)); // if (tempFont == null) // { // // Load Font Asset - // tempFont = Resources.Load(TMP_Settings.defaultFontAssetPath + new string(calliString, firstAttribute.valueStartIndex, firstAttribute.valueLength)); + // tempFont = Resources.Load(TMP_Settings.defaultFontAssetPath + new string(calliString, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength)); // } // if (tempFont == null) @@ -657,7 +664,7 @@ internal static bool ValidateHtmlTag( // } // // HANDLE NEW MATERIAL - // if (materialAttributeHashCode == 0 && materialHashCode == 0) + // if (materialTagIndentifierHashCode == 0 && materialHashCode == 0) // { // // No material specified then use default font asset material. // m_currentMaterial = tempFont.material; @@ -666,7 +673,7 @@ internal static bool ValidateHtmlTag( // m_materialReferenceStack.Add(m_materialReferences[m_currentMaterialIndex]); // } - // else if (materialAttributeHashCode == 103415287 || materialAttributeHashCode == 72669687) // using material attribute + // else if (materialTagIndentifierHashCode == 103415287 || materialTagIndentifierHashCode == 72669687) // using material tagIndentifier // { // if (MaterialReferenceManager.TryGetMaterial(materialHashCode, out tempMaterial)) // { @@ -679,7 +686,7 @@ internal static bool ValidateHtmlTag( // else // { // // Load new material - // tempMaterial = Resources.Load(TMP_Settings.defaultFontAssetPath + new string(calliString, richTextAttributes[1].valueStartIndex, richTextAttributes[1].valueLength)); + // tempMaterial = Resources.Load(TMP_Settings.defaultFontAssetPath + new string(calliString, richTextTagIndentifiers[1].valueStartIndex, richTextTagIndentifiers[1].valueLength)); // if (tempMaterial == null) // return false; @@ -713,7 +720,7 @@ internal static bool ValidateHtmlTag( // } //case 103415287: // //case 72669687: // - // materialHashCode = firstAttribute.valueHashCode; + // materialHashCode = firstTagIndentifier.valueHashCode; // // Special handling for or // if (materialHashCode == 764638571 || materialHashCode == 523367755) @@ -729,7 +736,6 @@ internal static bool ValidateHtmlTag( // return true; // } - // // Check if material // if (MaterialReferenceManager.TryGetMaterial(materialHashCode, out tempMaterial)) // { @@ -745,7 +751,7 @@ internal static bool ValidateHtmlTag( // else // { // // Load new material - // tempMaterial = Resources.Load(TMP_Settings.defaultFontAssetPath + new string(calliString, firstAttribute.valueStartIndex, firstAttribute.valueLength)); + // tempMaterial = Resources.Load(TMP_Settings.defaultFontAssetPath + new string(calliString, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength)); // if (tempMaterial == null) // return false; @@ -776,10 +782,10 @@ internal static bool ValidateHtmlTag( // return true; // } - case 320078: // - case 230446: // - firstAttribute.tagType = RichTextTagType.Space; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case 320078: // + case 230446: // + firstTagIndentifier.tagType = RichTextTagType.Space; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -797,12 +803,13 @@ internal static bool ValidateHtmlTag( return false; } return false; - case 276254: // - case 186622: // - firstAttribute.tagType = RichTextTagType.Alpha; - if (firstAttribute.valueLength != 3) return false; + case 276254: // + case 186622: // + firstTagIndentifier.tagType = RichTextTagType.Alpha; + if (firstTagIndentifier.valueLength != 3) + return false; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + 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])); return true; @@ -820,12 +827,12 @@ internal static bool ValidateHtmlTag( // TMP_TextInfo.Resize(ref m_textInfo.linkInfo, index + 1); // m_textInfo.linkInfo[index].textComponent = this; - // m_textInfo.linkInfo[index].hashCode = firstAttribute.valueHashCode; + // m_textInfo.linkInfo[index].hashCode = firstTagIndentifier.valueHashCode; // m_textInfo.linkInfo[index].linkTextfirstCharacterIndex = m_characterCount; - // m_textInfo.linkInfo[index].linkIdFirstCharacterIndex = startIndex + firstAttribute.valueStartIndex; - // m_textInfo.linkInfo[index].linkIdLength = firstAttribute.valueLength; - // m_textInfo.linkInfo[index].SetLinkID(calliString, firstAttribute.valueStartIndex, firstAttribute.valueLength); + // m_textInfo.linkInfo[index].linkIdFirstCharacterIndex = startIndex + firstTagIndentifier.valueStartIndex; + // m_textInfo.linkInfo[index].linkIdLength = firstTagIndentifier.valueLength; + // m_textInfo.linkInfo[index].SetLinkID(calliString, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // } // return true; //case 155913: // @@ -840,40 +847,40 @@ internal static bool ValidateHtmlTag( // } // } // return true; - case 275917: // - case 186285: // - switch (firstAttribute.valueHashCode) + case 275917: // + case 186285: // + switch (firstTagIndentifier.valueHashCode) { - case 3774683: // + case 3774683: // textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Left; textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); return true; - case 136703040: // + case 136703040: // textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Right; textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); return true; - case -458210101: // + case -458210101: // textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Center; textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); return true; - case -523808257: // + case -523808257: // textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Justified; textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); return true; - case 122383428: // + case 122383428: // textConfiguration.m_lineJustification = HorizontalAlignmentOptions.Flush; textConfiguration.m_lineJustificationStack.Add(textConfiguration.m_lineJustification); return true; } return false; - case 1065846: // - case 976214: // + case 1065846: // + case 976214: // textConfiguration.m_lineJustification = textConfiguration.m_lineJustificationStack.RemoveExceptRoot(); return true; - case 327550: // - case 237918: // - firstAttribute.tagType = RichTextTagType.Width; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case 327550: // + case 237918: // + firstTagIndentifier.tagType = RichTextTagType.Width; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -891,16 +898,16 @@ internal static bool ValidateHtmlTag( break; } return true; - case 1117479: // - case 1027847: // + case 1117479: // + case 1027847: // textConfiguration.m_width = -1; return true; - case 281955: // or - case 192323: // - // 3 Hex (short hand) - firstAttribute.tagType = RichTextTagType.Color; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); - tagCharCount -= 6; //remove "color=" from char count + case 281955: // or + case 192323: // + // 3 Hex (short hand) + firstTagIndentifier.tagType = RichTextTagType.Color; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + tagCharCount -= 6; //remove "color=" from char count if (textConfiguration.m_htmlTag[0] == 35 && tagCharCount == 4) { textConfiguration.m_htmlColor = HexCharsToColor(textConfiguration.m_htmlTag, tagCharCount); @@ -930,45 +937,45 @@ internal static bool ValidateHtmlTag( } // - switch (firstAttribute.valueHashCode) + switch (firstTagIndentifier.valueHashCode) { - case 125395: // + case 125395: // textConfiguration.m_htmlColor = Color.red; textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); return true; - case -992792864: // + case -992792864: // textConfiguration.m_htmlColor = new Color32(173, 216, 230, 255); textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); return true; - case 3573310: // + case 3573310: // textConfiguration.m_htmlColor = Color.blue; textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); return true; - case 3680713: // + case 3680713: // textConfiguration.m_htmlColor = new Color32(128, 128, 128, 255); textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); return true; - case 117905991: // + case 117905991: // textConfiguration.m_htmlColor = Color.black; textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); return true; - case 121463835: // + case 121463835: // textConfiguration.m_htmlColor = Color.green; textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); return true; - case 140357351: // + case 140357351: // textConfiguration.m_htmlColor = Color.white; textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); return true; - case 26556144: // + case 26556144: // textConfiguration.m_htmlColor = new Color32(255, 128, 0, 255); textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); return true; - case -36881330: // + case -36881330: // textConfiguration.m_htmlColor = new Color32(160, 32, 240, 255); textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); return true; - case 554054276: // + case 554054276: // textConfiguration.m_htmlColor = Color.yellow; textConfiguration.m_colorStack.Add(textConfiguration.m_htmlColor); return true; @@ -977,9 +984,9 @@ internal static bool ValidateHtmlTag( //case 100149144: // //case 69403544: // - // firstAttribute.tagType = RichTextTagType.Gradient; - // calliString.GetSubString(ref richtextAdjustments.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); - // int gradientPresetHashCode = firstAttribute.valueHashCode; + // firstTagIndentifier.tagType = RichTextTagType.Gradient; + // calliString.GetSubString(ref richtextAdjustments.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); + // int gradientPresetHashCode = firstTagIndentifier.valueHashCode; // //TMP_ColorGradient tempColorGradientPreset; // //// Check if Color Gradient Preset has already been loaded. @@ -992,7 +999,7 @@ internal static bool ValidateHtmlTag( // // // Load Color Gradient Preset // // if (tempColorGradientPreset == null) // // { - // // tempColorGradientPreset = Resources.Load(TMP_Settings.defaultColorGradientPresetsPath + new string(calliString, firstAttribute.valueStartIndex, firstAttribute.valueLength)); + // // tempColorGradientPreset = Resources.Load(TMP_Settings.defaultColorGradientPresetsPath + new string(calliString, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength)); // // } // // if (tempColorGradientPreset == null) @@ -1004,17 +1011,17 @@ internal static bool ValidateHtmlTag( // richtextAdjustments.m_colorGradientPresetIsTinted = false; - // // Check Attributes - // for (int i = 1; i < richTextAttributes.Length && richTextAttributes[i].nameHashCode != 0; i++) + // // Check TagIndentifiers + // for (int i = 1; i < richTextTagIndentifiers.Length && richTextTagIndentifiers[i].nameHashCode != 0; i++) // { - // // Get attribute name - // int nameHashCode = richTextAttributes[i].nameHashCode; + // // Get tagIndentifier name + // int nameHashCode = richTextTagIndentifiers[i].nameHashCode; // switch (nameHashCode) // { // case 45819: // tint // case 33019: // TINT - // calliString.GetSubString(ref richtextAdjustments.m_htmlTag, richTextAttributes[i].valueStartIndex, richTextAttributes[i].valueLength); + // calliString.GetSubString(ref richtextAdjustments.m_htmlTag, richTextTagIndentifiers[i].valueStartIndex, richTextTagIndentifiers[i].valueLength); // if (ConvertToFloat(ref richtextAdjustments.m_htmlTag, out value) != ParseError.None) // richtextAdjustments.m_colorGradientPresetIsTinted = value != 0; // break; @@ -1031,10 +1038,10 @@ internal static bool ValidateHtmlTag( // m_colorGradientPreset = m_colorGradientStack.Remove(); // return true; - case 1983971: // - case 1356515: // - firstAttribute.tagType = RichTextTagType.CharacterSpacing; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case 1983971: // + case 1356515: // + firstTagIndentifier.tagType = RichTextTagType.CharacterSpacing; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -1051,9 +1058,10 @@ internal static bool ValidateHtmlTag( return false; } return true; - case 7513474: // - case 6886018: // - if (!textConfiguration.m_isParsingText) return true; + case 7513474: // + case 6886018: // + if (!textConfiguration.m_isParsingText) + return true; // Adjust xAdvance to remove extra space from last character. if (textConfiguration.m_characterCount > 0) @@ -1063,10 +1071,10 @@ internal static bool ValidateHtmlTag( } textConfiguration.m_cSpacing = 0; return true; - case 2152041: // - case 1524585: // - firstAttribute.tagType = RichTextTagType.MonoSpace; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case 2152041: // + case 1524585: // + firstTagIndentifier.tagType = RichTextTagType.MonoSpace; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -1083,20 +1091,20 @@ internal static bool ValidateHtmlTag( return false; } return true; - case 7681544: // - case 7054088: // + case 7681544: // + case 7054088: // textConfiguration.m_monoSpacing = 0; return true; - case 280416: // + case 280416: // return false; - case 1071884: // - case 982252: // + case 1071884: // + case 982252: // textConfiguration.m_htmlColor = textConfiguration.m_colorStack.RemoveExceptRoot(); return true; - case 2068980: // - case 1441524: // - firstAttribute.tagType = RichTextTagType.Indent; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case 2068980: // + case 1441524: // + firstTagIndentifier.tagType = RichTextTagType.Indent; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -1118,15 +1126,15 @@ internal static bool ValidateHtmlTag( textConfiguration.m_xAdvance = textConfiguration.tag_Indent; return true; - case 7598483: // - case 6971027: // + case 7598483: // + case 6971027: // textConfiguration.tag_Indent = textConfiguration.m_indentStack.RemoveExceptRoot(); //m_xAdvance = tag_Indent; return true; - case 1109386397: // - case -842656867: // - firstAttribute.tagType = RichTextTagType.LineIndent; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case 1109386397: // + case -842656867: // + firstTagIndentifier.tagType = RichTextTagType.LineIndent; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -1146,18 +1154,18 @@ internal static bool ValidateHtmlTag( textConfiguration.m_xAdvance += textConfiguration.tag_LineIndent; return true; - case -445537194: // - case 1897386838: // + case -445537194: // + case 1897386838: // textConfiguration.tag_LineIndent = 0; return true; //case 2246877: // //case 1619421: // - // int spriteAssetHashCode = firstAttribute.valueHashCode; + // int spriteAssetHashCode = firstTagIndentifier.valueHashCode; // TMP_SpriteAsset tempSpriteAsset; // m_spriteIndex = -1; // // CHECK TAG FORMAT - // if (firstAttribute.valueType == TagValueType.None || firstAttribute.valueType == TagValueType.NumericalValue) + // if (firstTagIndentifier.valueType == TagValueType.None || firstTagIndentifier.valueType == TagValueType.NumericalValue) // { // // No Sprite Asset is assigned to the text object // if (m_spriteAsset != null) @@ -1195,10 +1203,10 @@ internal static bool ValidateHtmlTag( // if (tempSpriteAsset == null) // { // // - // tempSpriteAsset = OnSpriteAssetRequest?.Invoke(spriteAssetHashCode, new string(calliString, firstAttribute.valueStartIndex, firstAttribute.valueLength)); + // tempSpriteAsset = OnSpriteAssetRequest?.Invoke(spriteAssetHashCode, new string(calliString, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength)); // if (tempSpriteAsset == null) - // tempSpriteAsset = Resources.Load(TMP_Settings.defaultSpriteAssetPath + new string(calliString, firstAttribute.valueStartIndex, firstAttribute.valueLength)); + // tempSpriteAsset = Resources.Load(TMP_Settings.defaultSpriteAssetPath + new string(calliString, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength)); // } // if (tempSpriteAsset == null) @@ -1211,7 +1219,7 @@ internal static bool ValidateHtmlTag( // } // // Handling of legacy tag format. - // if (firstAttribute.valueType == TagValueType.NumericalValue) // + // if (firstTagIndentifier.valueType == TagValueType.NumericalValue) // // { // int index = (int)ConvertToFloat(ref richtextAdjustments.m_htmlTag, out value); @@ -1227,25 +1235,25 @@ internal static bool ValidateHtmlTag( // m_spriteColor = s_colorWhite; // m_tintSprite = false; - // // Handle Sprite Tag Attributes - // for (int i = 0; i < richTextAttributes.Length && richTextAttributes[i].nameHashCode != 0; i++) + // // Handle Sprite Tag TagIndentifiers + // for (int i = 0; i < richTextTagIndentifiers.Length && richTextTagIndentifiers[i].nameHashCode != 0; i++) // { - // //Debug.Log("Attribute[" + i + "].nameHashCode=" + richTextAttributes[i].nameHashCode + " Value:" + ConvertToFloat(ref richtextAdjustments.m_htmlTag, richTextAttributes[i].valueStartIndex, richTextAttributes[i].valueLength)); - // int nameHashCode = richTextAttributes[i].nameHashCode; + // //Debug.Log("TagIndentifier[" + i + "].nameHashCode=" + richTextTagIndentifiers[i].nameHashCode + " Value:" + ConvertToFloat(ref richtextAdjustments.m_htmlTag, richTextTagIndentifiers[i].valueStartIndex, richTextTagIndentifiers[i].valueLength)); + // int nameHashCode = richTextTagIndentifiers[i].nameHashCode; // int index = 0; // switch (nameHashCode) // { // case 43347: // // case 30547: // - // m_currentSpriteAsset = TMP_SpriteAsset.SearchForSpriteByHashCode(m_currentSpriteAsset, richTextAttributes[i].valueHashCode, true, out index); + // m_currentSpriteAsset = TMP_SpriteAsset.SearchForSpriteByHashCode(m_currentSpriteAsset, richTextTagIndentifiers[i].valueHashCode, true, out index); // if (index == -1) return false; // m_spriteIndex = index; // break; // case 295562: // // case 205930: // - // index = (int)ConvertToFloat(ref richtextAdjustments.m_htmlTag, richTextAttributes[1].valueStartIndex, richTextAttributes[1].valueLength); + // index = (int)ConvertToFloat(ref richtextAdjustments.m_htmlTag, richTextTagIndentifiers[1].valueStartIndex, richTextTagIndentifiers[1].valueLength); // // Reject tag if value is invalid. // if (index == Int16.MinValue) return false; @@ -1257,19 +1265,19 @@ internal static bool ValidateHtmlTag( // break; // case 45819: // tint // case 33019: // TINT - // m_tintSprite = ConvertToFloat(ref richtextAdjustments.m_htmlTag, richTextAttributes[i].valueStartIndex, richTextAttributes[i].valueLength) != 0; + // m_tintSprite = ConvertToFloat(ref richtextAdjustments.m_htmlTag, richTextTagIndentifiers[i].valueStartIndex, richTextTagIndentifiers[i].valueLength) != 0; // break; // case 281955: // color=#FF00FF80 // case 192323: // COLOR - // m_spriteColor = HexCharsToColor(calliString, richTextAttributes[i].valueStartIndex, richTextAttributes[i].valueLength); + // m_spriteColor = HexCharsToColor(calliString, richTextTagIndentifiers[i].valueStartIndex, richTextTagIndentifiers[i].valueLength); // break; // case 39505: // anim="0,16,12" start, end, fps // case 26705: // ANIM - // //Debug.Log("Start: " + richTextAttributes[i].valueStartIndex + " Length: " + richTextAttributes[i].valueLength); - // int paramCount = GetAttributeParameters(calliString, richTextAttributes[i].valueStartIndex, richTextAttributes[i].valueLength, ref m_attributeParameterValues); + // //Debug.Log("Start: " + richTextTagIndentifiers[i].valueStartIndex + " Length: " + richTextTagIndentifiers[i].valueLength); + // int paramCount = GetTagIndentifierParameters(calliString, richTextTagIndentifiers[i].valueStartIndex, richTextTagIndentifiers[i].valueLength, ref m_tagIndentifierParameterValues); // if (paramCount != 3) return false; - // m_spriteIndex = (int)m_attributeParameterValues[0]; + // m_spriteIndex = (int)m_tagIndentifierParameterValues[0]; // if (m_isParsingText) // { @@ -1277,7 +1285,7 @@ internal static bool ValidateHtmlTag( // // It is possible for a sprite to get animated when it ends up being truncated. // // Should consider moving the animation of the sprite after text geometry upload. - // spriteAnimator.DoSpriteAnimation(m_characterCount, m_currentSpriteAsset, m_spriteIndex, (int)m_attributeParameterValues[1], (int)m_attributeParameterValues[2]); + // spriteAnimator.DoSpriteAnimation(m_characterCount, m_currentSpriteAsset, m_spriteIndex, (int)m_tagIndentifierParameterValues[1], (int)m_tagIndentifierParameterValues[2]); // } // break; @@ -1299,57 +1307,57 @@ internal static bool ValidateHtmlTag( // m_textElementType = TMP_TextElementType.Sprite; // return true; - case 730022849: // - case 514803617: // - textConfiguration.m_FontStyleInternal |= FontStyles.LowerCase; + case 730022849: // + case 514803617: // + textConfiguration.m_fontStyleInternal |= FontStyles.LowerCase; textConfiguration.m_fontStyleStack.Add(FontStyles.LowerCase); return true; - case -1668324918: // - case -1883544150: // + case -1668324918: // + case -1883544150: // if ((baseConfiguration.fontStyle & FontStyles.LowerCase) != FontStyles.LowerCase) { if (textConfiguration.m_fontStyleStack.Remove(FontStyles.LowerCase) == 0) - textConfiguration.m_FontStyleInternal &= ~FontStyles.LowerCase; + textConfiguration.m_fontStyleInternal &= ~FontStyles.LowerCase; } return true; - case 13526026: // - case 9133802: // - case 781906058: // - case 566686826: // - textConfiguration.m_FontStyleInternal |= FontStyles.UpperCase; + case 13526026: // + case 9133802: // + case 781906058: // + case 566686826: // + textConfiguration.m_fontStyleInternal |= FontStyles.UpperCase; textConfiguration.m_fontStyleStack.Add(FontStyles.UpperCase); return true; - case 52232547: // - case 47840323: // - case -1616441709: // - case -1831660941: // + case 52232547: // + case 47840323: // + case -1616441709: // + case -1831660941: // if ((baseConfiguration.fontStyle & FontStyles.UpperCase) != FontStyles.UpperCase) { if (textConfiguration.m_fontStyleStack.Remove(FontStyles.UpperCase) == 0) - textConfiguration.m_FontStyleInternal &= ~FontStyles.UpperCase; + textConfiguration.m_fontStyleInternal &= ~FontStyles.UpperCase; } return true; - case 766244328: // - case 551025096: // - textConfiguration.m_FontStyleInternal |= FontStyles.SmallCaps; + case 766244328: // + case 551025096: // + textConfiguration.m_fontStyleInternal |= FontStyles.SmallCaps; textConfiguration.m_fontStyleStack.Add(FontStyles.SmallCaps); return true; - case -1632103439: // - case -1847322671: // + case -1632103439: // + case -1847322671: // if ((baseConfiguration.fontStyle & FontStyles.SmallCaps) != FontStyles.SmallCaps) { if (textConfiguration.m_fontStyleStack.Remove(FontStyles.SmallCaps) == 0) - textConfiguration.m_FontStyleInternal &= ~FontStyles.SmallCaps; + textConfiguration.m_fontStyleInternal &= ~FontStyles.SmallCaps; } return true; - case 2109854: // - case 1482398: // - // Check value type - switch (firstAttribute.valueType) + case 2109854: // + case 1482398: // + // Check value type + switch (firstTagIndentifier.valueType) { case TagValueType.NumericalValue: - firstAttribute.tagType = RichTextTagType.Margin; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + firstTagIndentifier.tagType = RichTextTagType.Margin; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -1364,30 +1372,31 @@ internal static bool ValidateHtmlTag( textConfiguration.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.m_marginLeft = (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; + textConfiguration.m_marginLeft = + (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; break; } - textConfiguration.m_marginLeft = textConfiguration.m_marginLeft >= 0 ? textConfiguration.m_marginLeft : 0; + textConfiguration.m_marginLeft = textConfiguration.m_marginLeft >= 0 ? textConfiguration.m_marginLeft : 0; textConfiguration.m_marginRight = textConfiguration.m_marginLeft; return true; case TagValueType.None: - for (int i = 1; i < richTextAttributes.Length && richTextAttributes[i].nameHashCode != 0; i++) + for (int i = 1; i < richTextTagIndentifiers.Length && richTextTagIndentifiers[i].nameHashCode != 0; i++) { - currentAttribute = ref richTextAttributes.ElementAt(i); - // Get attribute name - int nameHashCode = richTextAttributes[i].nameHashCode; + currentTagIndentifier = ref richTextTagIndentifiers.ElementAt(i); + // Get tagIndentifier name + int nameHashCode = richTextTagIndentifiers[i].nameHashCode; switch (nameHashCode) { case 42823: // - currentAttribute.tagType = RichTextTagType.Margin; - calliString.GetSubString(ref textConfiguration.m_htmlTag, currentAttribute.valueStartIndex, currentAttribute.valueLength); + currentTagIndentifier.tagType = RichTextTagType.Margin; + calliString.GetSubString(ref textConfiguration.m_htmlTag, currentTagIndentifier.valueStartIndex, currentTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; - switch (richTextAttributes[i].unitType) + switch (richTextTagIndentifiers[i].unitType) { case TagUnitType.Pixels: textConfiguration.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); @@ -1396,20 +1405,21 @@ internal static bool ValidateHtmlTag( textConfiguration.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.m_marginLeft = (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; + textConfiguration.m_marginLeft = + (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; break; } textConfiguration.m_marginLeft = textConfiguration.m_marginLeft >= 0 ? textConfiguration.m_marginLeft : 0; break; - case 315620: // - currentAttribute.tagType = RichTextTagType.Margin; - calliString.GetSubString(ref textConfiguration.m_htmlTag, currentAttribute.valueStartIndex, currentAttribute.valueLength); + case 315620: // + currentTagIndentifier.tagType = RichTextTagType.Margin; + calliString.GetSubString(ref textConfiguration.m_htmlTag, currentTagIndentifier.valueStartIndex, currentTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; - switch (richTextAttributes[i].unitType) + switch (richTextTagIndentifiers[i].unitType) { case TagUnitType.Pixels: textConfiguration.m_marginRight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f); @@ -1418,7 +1428,8 @@ internal static bool ValidateHtmlTag( textConfiguration.m_marginRight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.m_marginRight = (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; + textConfiguration.m_marginRight = + (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; break; } textConfiguration.m_marginRight = textConfiguration.m_marginRight >= 0 ? textConfiguration.m_marginRight : 0; @@ -1429,15 +1440,15 @@ internal static bool ValidateHtmlTag( } return false; - case 7639357: // - case 7011901: // - textConfiguration.m_marginLeft = 0; + case 7639357: // + case 7011901: // + textConfiguration.m_marginLeft = 0; textConfiguration.m_marginRight = 0; return true; - case 1100728678: // - case -855002522: // - firstAttribute.tagType = RichTextTagType.Margin; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case 1100728678: // + case -855002522: // + firstTagIndentifier.tagType = RichTextTagType.Margin; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -1451,15 +1462,16 @@ internal static bool ValidateHtmlTag( textConfiguration.m_marginLeft = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.m_marginLeft = (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; + textConfiguration.m_marginLeft = + (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; break; } textConfiguration.m_marginLeft = textConfiguration.m_marginLeft >= 0 ? textConfiguration.m_marginLeft : 0; return true; - case -884817987: // - case -1690034531: // - firstAttribute.tagType = RichTextTagType.Margin; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case -884817987: // + case -1690034531: // + firstTagIndentifier.tagType = RichTextTagType.Margin; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -1473,15 +1485,16 @@ internal static bool ValidateHtmlTag( textConfiguration.m_marginRight = value * (baseConfiguration.isOrthographic ? 1 : 0.1f) * textConfiguration.m_currentFontSize; break; case TagUnitType.Percentage: - textConfiguration.m_marginRight = (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; + textConfiguration.m_marginRight = + (textConfiguration.m_marginWidth - (textConfiguration.m_width != -1 ? textConfiguration.m_width : 0)) * value / 100; break; } textConfiguration.m_marginRight = textConfiguration.m_marginRight >= 0 ? textConfiguration.m_marginRight : 0; return true; - case 1109349752: // - case -842693512: // - firstAttribute.tagType = RichTextTagType.LineHeight; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case 1109349752: // + case -842693512: // + firstTagIndentifier.tagType = RichTextTagType.LineHeight; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; @@ -1500,17 +1513,17 @@ internal static bool ValidateHtmlTag( break; } return true; - case -445573839: // - case 1897350193: // - textConfiguration.m_lineHeight = float.MinValue; //TMP_Math.FLOAT_UNSET -->is there a better way to do this? + case -445573839: // + case 1897350193: // + textConfiguration.m_lineHeight = float.MinValue; //TMP_Math.FLOAT_UNSET -->is there a better way to do this? return true; - case 15115642: // - case 10723418: // + case 15115642: // + case 10723418: // textConfiguration.tag_NoParsing = true; return true; //case 1913798: // //case 1286342: // - // int actionID = firstAttribute.valueHashCode; + // int actionID = firstTagIndentifier.valueHashCode; // if (m_isParsingText) // { @@ -1518,11 +1531,10 @@ internal static bool ValidateHtmlTag( // Debug.Log("Action ID: [" + actionID + "] First character index: " + m_characterCount); - // } // //if (m_isParsingText) // //{ - // // TMP_Action action = TMP_Action.GetAction(firstAttribute.valueHashCode); + // // TMP_Action action = TMP_Action.GetAction(firstTagIndentifier.valueHashCode); // //} // return true; //case 7443301: // @@ -1549,29 +1561,29 @@ internal static bool ValidateHtmlTag( //case 1015979: // // m_isFXMatrixSet = false; // return true; - case 2227963: // - case 1600507: // - // TODO: Add ability to use Random Rotation - firstAttribute.tagType = RichTextTagType.Rotate; - calliString.GetSubString(ref textConfiguration.m_htmlTag, firstAttribute.valueStartIndex, firstAttribute.valueLength); + case 2227963: // + case 1600507: // + // TODO: Add ability to use Random Rotation + firstTagIndentifier.tagType = RichTextTagType.Rotate; + calliString.GetSubString(ref textConfiguration.m_htmlTag, firstTagIndentifier.valueStartIndex, firstTagIndentifier.valueLength); // Reject tag if value is invalid. if (ConvertToFloat(ref textConfiguration.m_htmlTag, out value) != ParseError.None) return false; - textConfiguration.m_FXMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0, 0, value), Vector3.one); + textConfiguration.m_fxMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0, 0, value), Vector3.one); textConfiguration.m_isFXMatrixSet = true; return true; - case 7757466: // - case 7130010: // + case 7757466: // + case 7130010: // textConfiguration.m_isFXMatrixSet = false; return true; - case 317446: // - case 227814: //
- //switch (richTextAttributes[1].nameHashCode) + case 317446: //
+ case 227814: //
+ //switch (richTextTagIndentifiers[1].nameHashCode) //{ // case 327550: // width - // float tableWidth = ConvertToFloat(ref richtextAdjustments.m_htmlTag, richTextAttributes[1].valueStartIndex, richTextAttributes[1].valueLength); + // float tableWidth = ConvertToFloat(ref richtextAdjustments.m_htmlTag, richTextTagIndentifiers[1].valueStartIndex, richTextTagIndentifiers[1].valueLength); // // Reject tag if value is invalid. // if (tableWidth == Int16.MinValue) return false; @@ -1591,31 +1603,31 @@ internal static bool ValidateHtmlTag( // break; //} return false; - case 1107375: //
- case 1017743: // + case 1107375: // + case 1017743: // return true; - case 926: // - case 670: // + case 926: // + case 670: // return true; - case 3229: // - case 2973: // + case 3229: // + case 2973: // return true; - case 916: // - case 660: // - // Set style to bold and center alignment + case 916: // + case 660: // + // Set style to bold and center alignment return true; - case 3219: // - case 2963: // + case 3219: // + case 2963: // return true; - case 912: // - case 656: // + case 912: // + case 656: // // Style options - //for (int i = 1; i < richTextAttributes.Length && richTextAttributes[i].nameHashCode != 0; i++) + //for (int i = 1; i < richTextTagIndentifiers.Length && richTextTagIndentifiers[i].nameHashCode != 0; i++) //{ - // switch (richTextAttributes[i].nameHashCode) + // switch (richTextTagIndentifiers[i].nameHashCode) // { // case 327550: // width - // float tableWidth = ConvertToFloat(ref richtextAdjustments.m_htmlTag, richTextAttributes[i].valueStartIndex, richTextAttributes[i].valueLength); + // float tableWidth = ConvertToFloat(ref richtextAdjustments.m_htmlTag, richTextTagIndentifiers[i].valueStartIndex, richTextTagIndentifiers[i].valueLength); // switch (tagUnitType) // { @@ -1631,7 +1643,7 @@ internal static bool ValidateHtmlTag( // } // break; // case 275917: // align - // switch (richTextAttributes[i].valueHashCode) + // switch (richTextTagIndentifiers[i].valueHashCode) // { // case 3774683: // left // Debug.Log("TD align=\"left\"."); @@ -1651,8 +1663,8 @@ internal static bool ValidateHtmlTag( //} return false; - case 3215: // - case 2959: // + case 3215: // + case 2959: // return false; } } @@ -1662,7 +1674,6 @@ internal static bool ValidateHtmlTag( return false; } - /// /// Extracts a float value from char[] given a start index and length. /// @@ -1670,12 +1681,12 @@ internal static bool ValidateHtmlTag( /// static ParseError ConvertToFloat(ref FixedString128Bytes htmlTag, out float value) { - value = 0; + value = 0; int subOffset = 0; return htmlTag.Parse(ref subOffset, ref value); } /// - /// Method to convert Hex color values to Color32. Accessing CalliString via indexer[] + /// Method to convert Hex color values to Color32. Accessing CalliString via indexer[] /// works only when CalliString consists ONLY of 1 byte chars!!! /// /// diff --git a/Calligraphics/Internal/RichText/RichTextTagIdentifier.cs b/Calligraphics/Internal/RichText/RichTextTagIdentifier.cs new file mode 100644 index 0000000..5b28226 --- /dev/null +++ b/Calligraphics/Internal/RichText/RichTextTagIdentifier.cs @@ -0,0 +1,27 @@ +namespace Latios.Calligraphics.RichText +{ + // Often referred to as "attributes" though we refrain from that term due to aliasing + // with both graphics attributes and C# language attributes. + internal struct RichTextTagIdentifier + { + public int nameHashCode; + public int valueHashCode; + public int valueStartIndex; //bytes position, not char! + public int valueLength; //byte length, not char! + public TagUnitType unitType; + public TagValueType valueType; + public RichTextTagType tagType; + + public static RichTextTagIdentifier Empty => new RichTextTagIdentifier + { + tagType = RichTextTagType.INVALID, + nameHashCode = 0, + valueHashCode = 0, + valueStartIndex = 0, + valueLength = 0, + valueType = TagValueType.None, + unitType = TagUnitType.Pixels, + }; + } +} + diff --git a/Calligraphics/Internal/RichText/RichTextAttribute.cs.meta b/Calligraphics/Internal/RichText/RichTextTagIdentifier.cs.meta similarity index 100% rename from Calligraphics/Internal/RichText/RichTextAttribute.cs.meta rename to Calligraphics/Internal/RichText/RichTextTagIdentifier.cs.meta diff --git a/Calligraphics/Internal/RichText/RichTextTagType.cs b/Calligraphics/Internal/RichText/RichTextTagType.cs index eb50701..9be3a84 100644 --- a/Calligraphics/Internal/RichText/RichTextTagType.cs +++ b/Calligraphics/Internal/RichText/RichTextTagType.cs @@ -1,6 +1,6 @@ -namespace Latios.Calligraphics +namespace Latios.Calligraphics.RichText { - public enum RichTextTagType : byte + internal enum RichTextTagType : byte { INVALID, Anchor, // diff --git a/Calligraphics/Internal/RichText/TagUnitType.cs b/Calligraphics/Internal/RichText/TagUnitType.cs index f128497..e32b3c5 100644 --- a/Calligraphics/Internal/RichText/TagUnitType.cs +++ b/Calligraphics/Internal/RichText/TagUnitType.cs @@ -1,6 +1,6 @@ -namespace Latios.Calligraphics +namespace Latios.Calligraphics.RichText { - public enum TagUnitType + internal enum TagUnitType : byte { Pixels = 0x0, FontUnits = 0x1, diff --git a/Calligraphics/Internal/RichText/TagValueType.cs b/Calligraphics/Internal/RichText/TagValueType.cs index dd811ce..57b0fc5 100644 --- a/Calligraphics/Internal/RichText/TagValueType.cs +++ b/Calligraphics/Internal/RichText/TagValueType.cs @@ -1,6 +1,6 @@ -namespace Latios.Calligraphics +namespace Latios.Calligraphics.RichText { - public enum TagValueType : byte + internal enum TagValueType : byte { None, NumericalValue, diff --git a/Calligraphics/Internal/TextConfiguration.cs b/Calligraphics/Internal/TextConfiguration.cs new file mode 100644 index 0000000..557ca8a --- /dev/null +++ b/Calligraphics/Internal/TextConfiguration.cs @@ -0,0 +1,140 @@ +using Latios.Calligraphics.RichText; +using Unity.Collections; +using UnityEngine; +using UnityEngine.TextCore.Text; + +namespace Latios.Calligraphics +{ + internal struct TextConfiguration + { + /// + /// 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; + + //metrics + public float m_fontScaleMultiplier; // Used for handling of superscript and subscript. + public float m_currentFontSize; + public FixedStack512Bytes m_sizeStack; + + public FontStyles m_fontStyleInternal; + public FontWeight m_fontWeightInternal; + public FontStyleStack m_fontStyleStack; + public FixedStack512Bytes m_fontWeightStack; + + public HorizontalAlignmentOptions m_lineJustification; + public FixedStack512Bytes m_lineJustificationStack; + + public float m_baselineOffset; + public FixedStack512Bytes m_baselineOffsetStack; + + public Color32 m_htmlColor; + public Color32 m_underlineColor; + public Color32 m_strikethroughColor; + + public FixedStack512Bytes m_colorStack; + public FixedStack512Bytes m_strikethroughColorStack; + public FixedStack512Bytes m_underlineColorStack; + + public short m_italicAngle; + public FixedStack512Bytes m_italicAngleStack; + + public float m_lineOffset; + public float m_lineHeight; + + public float m_cSpacing; + public float m_monoSpacing; + public float m_xAdvance; + + public float tag_LineIndent; + public float tag_Indent; + public FixedStack512Bytes m_indentStack; + public bool tag_NoParsing; + + 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 bool m_isParsingText; + + public Matrix4x4 m_fxMatrix; + public bool m_isFXMatrixSet; + + public FixedStack512Bytes m_highlightStateStack; + public int m_characterCount; + + public TextConfiguration(TextBaseConfiguration textBaseConfiguration) + { + m_htmlTag = new FixedString128Bytes(); + + m_fontScaleMultiplier = 1; + m_currentFontSize = textBaseConfiguration.fontSize; + m_sizeStack = new FixedStack512Bytes(); + 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.Add(m_fontWeightInternal); + m_fontStyleStack = new FontStyleStack(); + + m_lineJustification = textBaseConfiguration.lineJustification; + m_lineJustificationStack = new FixedStack512Bytes(); + m_lineJustificationStack.Add(m_lineJustification); + + m_baselineOffset = 0; + m_baselineOffsetStack = new FixedStack512Bytes(); + m_baselineOffsetStack.Add(0); + + m_htmlColor = textBaseConfiguration.color; + m_underlineColor = Color.white; + m_strikethroughColor = Color.white; + + m_colorStack = new FixedStack512Bytes(); + m_colorStack.Add(m_htmlColor); + m_underlineColorStack = new FixedStack512Bytes(); + m_underlineColorStack.Add(m_htmlColor); + m_strikethroughColorStack = new FixedStack512Bytes(); + m_strikethroughColorStack.Add(m_htmlColor); + + m_italicAngle = 0; + m_italicAngleStack = new FixedStack512Bytes(); + + 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_marginWidth = 0; + m_marginHeight = 0; + m_marginLeft = 0; + m_marginRight = 0; + m_width = -1; + + m_isNonBreakingSpace = false; + + m_isParsingText = false; + m_fxMatrix = Matrix4x4.identity; + m_isFXMatrixSet = false; + + m_highlightStateStack = new FixedStack512Bytes(); + + m_characterCount = 0; // Total characters in the CalliString + } + } +} + diff --git a/Calligraphics/Components/TextConfiguration.cs.meta b/Calligraphics/Internal/TextConfiguration.cs.meta similarity index 100% rename from Calligraphics/Components/TextConfiguration.cs.meta rename to Calligraphics/Internal/TextConfiguration.cs.meta diff --git a/Calligraphics/ShaderLibrary/TextGlyphParsing.hlsl b/Calligraphics/ShaderLibrary/TextGlyphParsing.hlsl index 31efd83..2583286 100644 --- a/Calligraphics/ShaderLibrary/TextGlyphParsing.hlsl +++ b/Calligraphics/ShaderLibrary/TextGlyphParsing.hlsl @@ -14,9 +14,10 @@ struct GlyphVertex #if defined(UNITY_DOTS_INSTANCING_ENABLED) uniform ByteAddressBuffer _latiosTextBuffer; +uniform ByteAddressBuffer _latiosTextMaskBuffer; #endif -GlyphVertex sampleGlyph(uint vertexId, uint textBase, uint glyphCount) +GlyphVertex sampleGlyph(uint vertexId, uint textBase, uint glyphCount, uint maskBase) { GlyphVertex vertex = (GlyphVertex)0; #if defined(UNITY_DOTS_INSTANCING_ENABLED) @@ -26,15 +27,27 @@ GlyphVertex sampleGlyph(uint vertexId, uint textBase, uint glyphCount) vertex.position = asfloat(~0u); return vertex; } + uint glyphBase = 96 * (textBase + (vertexId >> 2)); + if (maskBase > 0) + { + uint mask = _latiosTextMaskBuffer.Load(4 * (maskBase + (vertexId >> 6))); + uint bit = (vertexId >> 2) & 0xf; + bit += 16; + if ((mask & (1 << bit)) == 0) + { + vertex.position = asfloat(~0u); + return vertex; + } + bit -= 16; + glyphBase = 96 * (textBase + (mask & 0xffff) + bit); + } const bool isBottomLeft = (vertexId & 0x3) == 0; const bool isTopLeft = (vertexId & 0x3) == 1; const bool isTopRight = (vertexId & 0x3) == 2; const bool isBottomRight = (vertexId & 0x3) == 3; - const uint glyphBase = 96 * (textBase + (vertexId >> 2)); const uint4 glyphMeta = _latiosTextBuffer.Load4(glyphBase + 80); - vertex.normal = float3(0, 0, -1); vertex.tangent = float3(1, 0, 0); diff --git a/Calligraphics/Systems/AnimateTextTransitionSystem.cs b/Calligraphics/Systems/AnimateTextTransitionSystem.cs index 3531b27..c1e5a3b 100644 --- a/Calligraphics/Systems/AnimateTextTransitionSystem.cs +++ b/Calligraphics/Systems/AnimateTextTransitionSystem.cs @@ -1,6 +1,5 @@ using Latios.Calligraphics.Rendering; using Latios.Calligraphics.RichText; -using Latios.Calligraphics.RichText.Parsing; using Unity.Burst; using Unity.Burst.Intrinsics; using Unity.Collections; diff --git a/Calligraphics/Systems/GenerateGlyphsSystem.cs b/Calligraphics/Systems/GenerateGlyphsSystem.cs index 249ac2f..cdfd327 100644 --- a/Calligraphics/Systems/GenerateGlyphsSystem.cs +++ b/Calligraphics/Systems/GenerateGlyphsSystem.cs @@ -3,6 +3,7 @@ using Unity.Burst.Intrinsics; using Unity.Collections; using Unity.Entities; +using Unity.Mathematics; using static Unity.Entities.SystemAPI; @@ -14,19 +15,21 @@ namespace Latios.Calligraphics.Systems [DisableAutoCreation] public partial struct GenerateGlyphsSystem : ISystem { - EntityQuery m_singleFontQuery; - EntityQuery m_multiFontQuery; + EntityQuery m_query; + + bool m_skipChangeFilter; [BurstCompile] public void OnCreate(ref SystemState state) { - m_singleFontQuery = state.Fluent() - .With(true) - .With(false) - .With(true) - .With(true) - .With(false) - .Build(); + m_query = state.Fluent() + .With( true) + .With( false) + .With( true) + .With(true) + .With( false) + .Build(); + m_skipChangeFilter = (state.WorldUnmanaged.Flags & WorldFlags.Editor) == WorldFlags.Editor; } [BurstCompile] @@ -34,54 +37,84 @@ public void OnUpdate(ref SystemState state) { state.Dependency = new Job { - calliByteHandle = GetBufferTypeHandle(true), - fontBlobReferenceHandle = GetComponentTypeHandle(true), - glyphMappingElementHandle = GetBufferTypeHandle(false), - glyphMappingMaskHandle = GetComponentTypeHandle(true), - renderGlyphHandle = GetBufferTypeHandle(false), + additionalEntitiesHandle = GetBufferTypeHandle(true), + calliByteHandle = GetBufferTypeHandle(true), + fontBlobReferenceHandle = GetComponentTypeHandle(true), + fontBlobReferenceLookup = GetComponentLookup(true), + glyphMappingElementHandle = GetBufferTypeHandle(false), + glyphMappingMaskHandle = GetComponentTypeHandle(true), + lastSystemVersion = m_skipChangeFilter ? 0 : state.LastSystemVersion, + renderGlyphHandle = GetBufferTypeHandle(false), + selectorHandle = GetBufferTypeHandle(false), textBaseConfigurationHandle = GetComponentTypeHandle(true), - textRenderControlHandle = GetComponentTypeHandle(false), - }.ScheduleParallel(m_singleFontQuery, state.Dependency); + textRenderControlHandle = GetComponentTypeHandle(false), + }.ScheduleParallel(m_query, state.Dependency); } [BurstCompile] public partial struct Job : IJobChunk { - public BufferTypeHandle renderGlyphHandle; - public BufferTypeHandle glyphMappingElementHandle; - public ComponentTypeHandle textRenderControlHandle; + public BufferTypeHandle renderGlyphHandle; + public BufferTypeHandle glyphMappingElementHandle; + public BufferTypeHandle selectorHandle; + public ComponentTypeHandle textRenderControlHandle; + + [ReadOnly] public ComponentTypeHandle glyphMappingMaskHandle; + [ReadOnly] public BufferTypeHandle calliByteHandle; + [ReadOnly] public ComponentTypeHandle textBaseConfigurationHandle; + [ReadOnly] public ComponentTypeHandle fontBlobReferenceHandle; + [ReadOnly] public BufferTypeHandle additionalEntitiesHandle; + [ReadOnly] public ComponentLookup fontBlobReferenceLookup; - [ReadOnly] public ComponentTypeHandle glyphMappingMaskHandle; - [ReadOnly] public BufferTypeHandle calliByteHandle; - [ReadOnly] public ComponentTypeHandle textBaseConfigurationHandle; - [ReadOnly] public ComponentTypeHandle fontBlobReferenceHandle; + public uint lastSystemVersion; private GlyphMappingWriter m_glyphMappingWriter; [BurstCompile] public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { - var calliBytesBuffers = chunk.GetBufferAccessor(ref calliByteHandle); - var renderGlyphBuffers = chunk.GetBufferAccessor(ref renderGlyphHandle); - var glyphMappingBuffers = chunk.GetBufferAccessor(ref glyphMappingElementHandle); - var glyphMappingMasks = chunk.GetNativeArray(ref glyphMappingMaskHandle); + if (!(chunk.DidChange(ref glyphMappingMaskHandle, lastSystemVersion) || + chunk.DidChange(ref calliByteHandle, lastSystemVersion) || + chunk.DidChange(ref textBaseConfigurationHandle, lastSystemVersion) || + chunk.DidChange(ref fontBlobReferenceHandle, lastSystemVersion))) + return; + + var calliBytesBuffers = chunk.GetBufferAccessor(ref calliByteHandle); + var renderGlyphBuffers = chunk.GetBufferAccessor(ref renderGlyphHandle); + var glyphMappingBuffers = chunk.GetBufferAccessor(ref glyphMappingElementHandle); + var glyphMappingMasks = chunk.GetNativeArray(ref glyphMappingMaskHandle); var textBaseConfigurations = chunk.GetNativeArray(ref textBaseConfigurationHandle); - var fontBlobReferences = chunk.GetNativeArray(ref fontBlobReferenceHandle); - var textRenderControls = chunk.GetNativeArray(ref textRenderControlHandle); + var fontBlobReferences = chunk.GetNativeArray(ref fontBlobReferenceHandle); + var textRenderControls = chunk.GetNativeArray(ref textRenderControlHandle); + + // Optional + var selectorBuffers = chunk.GetBufferAccessor(ref selectorHandle); + var additionalEntitiesBuffers = chunk.GetBufferAccessor(ref additionalEntitiesHandle); + bool hasMultipleFonts = selectorBuffers.Length > 0 && additionalEntitiesBuffers.Length > 0; + + FontMaterialSet fontMaterialSet = default; for (int indexInChunk = 0; indexInChunk < chunk.Count; indexInChunk++) { - var calliBytes = calliBytesBuffers[indexInChunk]; - var renderGlyphs = renderGlyphBuffers[indexInChunk]; - var fontBlobReference = fontBlobReferences[indexInChunk]; + var calliBytes = calliBytesBuffers[indexInChunk]; + var renderGlyphs = renderGlyphBuffers[indexInChunk]; + var fontBlobReference = fontBlobReferences[indexInChunk]; var textBaseConfiguration = textBaseConfigurations[indexInChunk]; - var textRenderControl = textRenderControls[indexInChunk]; + var textRenderControl = textRenderControls[indexInChunk]; m_glyphMappingWriter.StartWriter(glyphMappingMasks.Length > 0 ? glyphMappingMasks[indexInChunk].mask : default); + if (hasMultipleFonts) + { + fontMaterialSet.Initialize(fontBlobReference.blob, selectorBuffers[indexInChunk], additionalEntitiesBuffers[indexInChunk], ref fontBlobReferenceLookup); + } + else + { + fontMaterialSet.Initialize(fontBlobReference.blob); + } GlyphGeneration.CreateRenderGlyphs(ref renderGlyphs, ref m_glyphMappingWriter, - ref fontBlobReference.blob.Value, + ref fontMaterialSet, in calliBytes, in textBaseConfiguration); @@ -91,7 +124,7 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE m_glyphMappingWriter.EndWriter(ref mapping, renderGlyphs.Length); } - textRenderControl.flags = TextRenderControl.Flags.Dirty; + textRenderControl.flags = TextRenderControl.Flags.Dirty; textRenderControls[indexInChunk] = textRenderControl; } } diff --git a/Calligraphics/Systems/Rendering/TextRenderingDispatchSystem.cs b/Calligraphics/Systems/Rendering/TextRenderingDispatchSystem.cs index b7bd1c0..3c96aea 100644 --- a/Calligraphics/Systems/Rendering/TextRenderingDispatchSystem.cs +++ b/Calligraphics/Systems/Rendering/TextRenderingDispatchSystem.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using Latios.Kinemation; using Latios.Kinemation.Systems; using Unity.Burst; @@ -22,9 +23,13 @@ namespace Latios.Calligraphics.Rendering.Systems [DisableAutoCreation] public partial class TextRenderingDispatchSystem : CullingComputeDispatchSubSystemBase { - ComputeShader m_uploadShader; + ComputeShader m_uploadGlyphsShader; + ComputeShader m_uploadMasksShader; - EntityQuery m_query; + EntityQuery m_glyphsQuery; + EntityQuery m_masksQuery; + EntityQuery m_allQuery; + EntityQuery m_glyphsAndMasksQuery; // Shader bindings int _src; @@ -33,28 +38,42 @@ public partial class TextRenderingDispatchSystem : CullingComputeDispatchSubSyst int _meta; int _latiosTextBuffer; + int _latiosTextMaskBuffer; - static GraphicsBufferBroker.StaticID kGlyphsBufferID = GraphicsBufferBroker.ReservePersistentBuffer(); - static GraphicsBufferBroker.StaticID kGlyphsUploadID = GraphicsBufferBroker.ReserveUploadPool(); + static GraphicsBufferBroker.StaticID kGlyphsBufferID = GraphicsBufferBroker.ReservePersistentBuffer(); + static GraphicsBufferBroker.StaticID kGlyphsUploadID = GraphicsBufferBroker.ReserveUploadPool(); + static GraphicsBufferBroker.StaticID kGlyphMasksBufferID = GraphicsBufferBroker.ReservePersistentBuffer(); + static GraphicsBufferBroker.StaticID kGlyphMasksUploadID = GraphicsBufferBroker.ReserveUploadPool(); protected override void OnCreate() { - m_query = Fluent.With(true).With(true).With(true) - .With(false, true).With(true, true).Build(); + m_glyphsQuery = Fluent.With(true).With(false) + .With( true, true).Build(); + m_masksQuery = Fluent.With(false).With(true) + .With( true, true).Build(); + m_allQuery = Fluent.WithAnyEnabled(true).With(true) + .With( false, true).With(true, true).Build(); + m_glyphsAndMasksQuery = Fluent.With(true) + .With(true) + .With( true, true).Build(); var copyByteAddressShader = Resources.Load("CopyBytes"); - m_uploadShader = Resources.Load("UploadGlyphs"); + m_uploadGlyphsShader = Resources.Load("UploadGlyphs"); + m_uploadMasksShader = Resources.Load("UploadBytes"); _src = Shader.PropertyToID("_src"); _dst = Shader.PropertyToID("_dst"); _startOffset = Shader.PropertyToID("_startOffset"); _meta = Shader.PropertyToID("_meta"); _latiosTextBuffer = Shader.PropertyToID("_latiosTextBuffer"); + _latiosTextMaskBuffer = Shader.PropertyToID("_latiosTextMaskBuffer"); if (!worldBlackboardEntity.HasManagedStructComponent()) throw new System.InvalidOperationException("Calligraphics must be installed after Kinemation."); var broker = worldBlackboardEntity.GetManagedStructComponent().graphicsBufferBroker; - broker.InitializePersistentBuffer(kGlyphsBufferID, 128, 4, GraphicsBuffer.Target.Raw, copyByteAddressShader); + broker.InitializePersistentBuffer(kGlyphsBufferID, 128 * 96, 4, GraphicsBuffer.Target.Raw, copyByteAddressShader); broker.InitializeUploadPool(kGlyphsUploadID, 4, GraphicsBuffer.Target.Raw); + broker.InitializePersistentBuffer(kGlyphMasksBufferID, 128 * 4, 4, GraphicsBuffer.Target.Raw, copyByteAddressShader); + broker.InitializeUploadPool(kGlyphMasksUploadID, 4, GraphicsBuffer.Target.Raw); } protected override IEnumerable UpdatePhase() @@ -69,38 +88,111 @@ protected override IEnumerable UpdatePhase() if (terminate) break; - int textIndex = worldBlackboardEntity.GetBuffer(true).Reinterpret() - .AsNativeArray().IndexOf(ComponentType.ReadOnly()); + var materials = worldBlackboardEntity.GetBuffer(true).Reinterpret().AsNativeArray(); + int textIndex = materials.IndexOf(ComponentType.ReadOnly()); ulong textMaterialMaskLower = (ulong)textIndex >= 64UL ? 0UL : (1UL << textIndex); ulong textMaterialMaskUpper = (ulong)textIndex >= 64UL ? (1UL << (textIndex - 64)) : 0UL; + int fontIndex = materials.IndexOf(ComponentType.ReadOnly()); + ulong fontMaterialMaskLower = (ulong)fontIndex >= 64UL ? 0UL : (1U << fontIndex); + ulong fontMaterialMaskUpper = (ulong)fontIndex >= 64UL ? (1UL << (fontIndex - 64)) : 0UL; - var streamCount = CollectionHelper.CreateNativeArray(1, WorldUpdateAllocator); - streamCount[0] = m_query.CalculateChunkCountWithoutFiltering(); - var streamConstructJh = NativeStream.ScheduleConstruct(out var stream, streamCount, default, WorldUpdateAllocator); - var collectJh = new GatherUploadOperationsJob + var materialMasksJh = new UpdateMaterialMasksJob { - glyphCountThisFrameLookup = SystemAPI.GetComponentLookup(false), - glyphCountThisPass = 0, + glyphMasksHandle = SystemAPI.GetBufferTypeHandle(true), + glyphMaterialMaskLower = textMaterialMaskLower, + glyphMaterialMaskUpper = textMaterialMaskUpper, glyphsHandle = SystemAPI.GetBufferTypeHandle(true), - materialMaskLower = textMaterialMaskLower, - materialMaskUpper = textMaterialMaskUpper, + maskMaterialMaskLower = fontMaterialMaskLower, + maskMaterialMaskUpper = fontMaterialMaskUpper, materialPropertyDirtyMaskHandle = SystemAPI.GetComponentTypeHandle(false), perCameraMaskHandle = SystemAPI.GetComponentTypeHandle(false), - perFrameMaskHandle = SystemAPI.GetComponentTypeHandle(true), - streamWriter = stream.AsWriter(), - textShaderIndexHandle = SystemAPI.GetComponentTypeHandle(false), - trcHandle = SystemAPI.GetComponentTypeHandle(true), - worldBlackboardEntity = worldBlackboardEntity - }.Schedule(m_query, JobHandle.CombineDependencies(streamConstructJh, Dependency)); - - var payloads = new NativeList(1, WorldUpdateAllocator); - var requiredUploadBufferSize = new NativeReference(WorldUpdateAllocator, NativeArrayOptions.UninitializedMemory); - Dependency = new MapPayloadsToUploadBufferJob + perFrameMaskHandle = SystemAPI.GetComponentTypeHandle(true) + }.ScheduleParallel(m_allQuery, Dependency); + + var foundChildrenDependenciesJh = materialMasksJh; + var glyphsWithChildrenCount = m_glyphsAndMasksQuery.CalculateChunkCountWithoutFiltering(); + var map = new NativeParallelHashMap(glyphsWithChildrenCount, WorldUpdateAllocator); + if (glyphsWithChildrenCount > 0) + { + foundChildrenDependenciesJh = new FindCulledGlyphHoldersWithVisibleChildrenJob + { + additionalEntitiesHandle = SystemAPI.GetBufferTypeHandle(true), + esil = SystemAPI.GetEntityStorageInfoLookup(), + map = map.AsParallelWriter(), + perCameraMaskHandle = SystemAPI.GetComponentTypeHandle(true), + perFrameMaskHandle = SystemAPI.GetComponentTypeHandle(true) + }.ScheduleParallel(m_glyphsAndMasksQuery, materialMasksJh); + } + + var glyphStreamCount = CollectionHelper.CreateNativeArray(1, WorldUpdateAllocator); + glyphStreamCount[0] = m_glyphsQuery.CalculateChunkCountWithoutFiltering(); + var glyphStreamConstructJh = NativeStream.ScheduleConstruct(out var glyphStream, glyphStreamCount, default, WorldUpdateAllocator); + var collectGlyphsJh = new GatherGlyphUploadOperationsJob + { + additonalEntitiesHandle = SystemAPI.GetBufferTypeHandle(true), + glyphCountThisFrameLookup = SystemAPI.GetComponentLookup(false), + glyphCountThisPass = 0, + glyphsHandle = SystemAPI.GetBufferTypeHandle(true), + glyphMaskHandle = SystemAPI.GetBufferTypeHandle(true), + map = map, + perCameraMaskHandle = SystemAPI.GetComponentTypeHandle(true), + perFrameMaskHandle = SystemAPI.GetComponentTypeHandle(true), + streamWriter = glyphStream.AsWriter(), + textShaderIndexHandle = SystemAPI.GetComponentTypeHandle(false), + trcHandle = SystemAPI.GetComponentTypeHandle(true), + worldBlackboardEntity = worldBlackboardEntity + }.Schedule(m_glyphsQuery, JobHandle.CombineDependencies(glyphStreamConstructJh, foundChildrenDependenciesJh)); + + var glyphPayloads = new NativeList(1, WorldUpdateAllocator); + var requiredGlyphUploadBufferSize = new NativeReference(WorldUpdateAllocator, NativeArrayOptions.UninitializedMemory); + var finalFirstPhaseJh = new MapPayloadsToUploadBufferJob { - streamReader = stream.AsReader(), - payloads = payloads, - requiredUploadBufferSize = requiredUploadBufferSize - }.Schedule(collectJh); + streamReader = glyphStream.AsReader(), + payloads = glyphPayloads, + requiredUploadBufferSize = requiredGlyphUploadBufferSize + }.Schedule(collectGlyphsJh); + + var maskPayloads = new NativeList(1, WorldUpdateAllocator); + var requiredMaskUploadBufferSize = new NativeReference(WorldUpdateAllocator, NativeArrayOptions.UninitializedMemory); + + if (glyphsWithChildrenCount > 0) + { + var maskStreamCount = CollectionHelper.CreateNativeArray(1, WorldUpdateAllocator); + maskStreamCount[0] = m_masksQuery.CalculateChunkCountWithoutFiltering(); + var maskStreamConstructJh = NativeStream.ScheduleConstruct(out var maskStream, maskStreamCount, default, WorldUpdateAllocator); + + var collectMasksJh = new GatherMaskUploadOperationsJob + { + glyphMasksHandle = SystemAPI.GetBufferTypeHandle(true), + maskCountThisFrameLookup = SystemAPI.GetComponentLookup(false), + maskCountThisPass = 0, + maskShaderIndexHandle = SystemAPI.GetComponentTypeHandle(false), + perCameraMaskHandle = SystemAPI.GetComponentTypeHandle(true), + perFrameMaskHandle = SystemAPI.GetComponentTypeHandle(true), + streamWriter = maskStream.AsWriter(), + worldBlackboardEntity = worldBlackboardEntity + }.Schedule(m_masksQuery, JobHandle.CombineDependencies(maskStreamConstructJh, materialMasksJh)); + + var batchMasksJh = new MapPayloadsToUploadBufferJob + { + streamReader = maskStream.AsReader(), + payloads = maskPayloads, + requiredUploadBufferSize = requiredMaskUploadBufferSize + }.Schedule(collectMasksJh); + + var copyPropertiesJh = new CopyGlyphShaderIndicesJob + { + additionalEntitiesHandle = SystemAPI.GetBufferTypeHandle(true), + perCameraMaskHandle = SystemAPI.GetComponentTypeHandle(true), + perFrameMaskHandle = SystemAPI.GetComponentTypeHandle(true), + shaderIndexHandle = SystemAPI.GetComponentTypeHandle(true), + shaderIndexLookup = SystemAPI.GetComponentLookup(false) + }.ScheduleParallel(m_glyphsAndMasksQuery, collectGlyphsJh); + + finalFirstPhaseJh = JobHandle.CombineDependencies(finalFirstPhaseJh, batchMasksJh, copyPropertiesJh); + } + + Dependency = finalFirstPhaseJh; // Fetching this now because culling jobs are still running (hopefully). var graphicsBroker = worldBlackboardEntity.GetManagedStructComponent().graphicsBufferBroker; @@ -112,7 +204,7 @@ protected override IEnumerable UpdatePhase() if (terminate) break; - if (payloads.IsEmpty) + if (glyphPayloads.IsEmpty) { // skip rest of loop. yield return true; @@ -126,43 +218,88 @@ protected override IEnumerable UpdatePhase() continue; } - var uploadBuffer = graphicsBroker.GetUploadBuffer(kGlyphsUploadID, math.max(requiredUploadBufferSize.Value, 128) * 24); - var metaBuffer = graphicsBroker.GetMetaUint4UploadBuffer((uint)payloads.Length); + var glyphUploadBuffer = graphicsBroker.GetUploadBuffer(kGlyphsUploadID, math.max(requiredGlyphUploadBufferSize.Value, 128) * 24); + var glyphMetaBuffer = graphicsBroker.GetMetaUint4UploadBuffer((uint)glyphPayloads.Length); - Dependency = new WriteUploadsToBuffersJob + var finalSecondPhaseJh = new WriteGlyphsUploadsToBuffersJob { - payloads = payloads.AsDeferredJobArray(), - glyphsUploadBuffer = uploadBuffer.LockBufferForWrite(0, (int)requiredUploadBufferSize.Value), - metaUploadBuffer = metaBuffer.LockBufferForWrite(0, payloads.Length) - }.Schedule(payloads, 1, Dependency); + payloads = glyphPayloads.AsDeferredJobArray(), + glyphsUploadBuffer = glyphUploadBuffer.LockBufferForWrite(0, (int)requiredGlyphUploadBufferSize.Value), + metaUploadBuffer = glyphMetaBuffer.LockBufferForWrite(0, glyphPayloads.Length) + }.Schedule(glyphPayloads, 1, Dependency); + + GraphicsBuffer maskUploadBuffer = default; + GraphicsBuffer maskMetaBuffer = default; + + if (glyphsWithChildrenCount > 0) + { + maskUploadBuffer = graphicsBroker.GetUploadBuffer(kGlyphMasksUploadID, math.max(requiredMaskUploadBufferSize.Value, 128)); + maskMetaBuffer = graphicsBroker.GetMetaUint3UploadBuffer((uint)maskPayloads.Length); + + var maskJh = new WriteMasksUploadsToBuffersJob + { + payloads = maskPayloads.AsDeferredJobArray(), + masksUploadBuffer = maskUploadBuffer.LockBufferForWrite(0, (int)requiredMaskUploadBufferSize.Value), + metaUploadBuffer = maskMetaBuffer.LockBufferForWrite(0, maskPayloads.Length) + }.Schedule(maskPayloads, 1, Dependency); + + finalSecondPhaseJh = JobHandle.CombineDependencies(finalSecondPhaseJh, maskJh); + } yield return true; if (!GetPhaseActions(CullingComputeDispatchState.Dispatch, out terminate)) continue; - uploadBuffer.UnlockBufferAfterWrite((int)requiredUploadBufferSize.Value); - metaBuffer.UnlockBufferAfterWrite(payloads.Length); + glyphUploadBuffer.UnlockBufferAfterWrite((int)requiredGlyphUploadBufferSize.Value); + glyphMetaBuffer.UnlockBufferAfterWrite(glyphPayloads.Length); + + if (glyphsWithChildrenCount > 0) + { + maskUploadBuffer.UnlockBufferAfterWrite((int)requiredMaskUploadBufferSize.Value); + maskMetaBuffer.UnlockBufferAfterWrite((int)maskPayloads.Length); + } if (terminate) break; - var persistentBuffer = graphicsBroker.GetPersistentBuffer(kGlyphsBufferID, - math.max(worldBlackboardEntity.GetComponentData().glyphCount, 128) * 24); - m_uploadShader.SetBuffer(0, _dst, persistentBuffer); - m_uploadShader.SetBuffer(0, _src, uploadBuffer); - m_uploadShader.SetBuffer(0, _meta, metaBuffer); + var persistentGlyphBuffer = graphicsBroker.GetPersistentBuffer(kGlyphsBufferID, + math.max(worldBlackboardEntity.GetComponentData().glyphCount, 128) * 24); + m_uploadGlyphsShader.SetBuffer(0, _dst, persistentGlyphBuffer); + m_uploadGlyphsShader.SetBuffer(0, _src, glyphUploadBuffer); + m_uploadGlyphsShader.SetBuffer(0, _meta, glyphMetaBuffer); - for (uint dispatchesRemaining = (uint)payloads.Length, offset = 0; dispatchesRemaining > 0;) + for (uint dispatchesRemaining = (uint)glyphPayloads.Length, offset = 0; dispatchesRemaining > 0;) { uint dispatchCount = math.min(dispatchesRemaining, 65535); - m_uploadShader.SetInt(_startOffset, (int)offset); - m_uploadShader.Dispatch(0, (int)dispatchCount, 1, 1); + m_uploadGlyphsShader.SetInt(_startOffset, (int)offset); + m_uploadGlyphsShader.Dispatch(0, (int)dispatchCount, 1, 1); offset += dispatchCount; dispatchesRemaining -= dispatchCount; } - Shader.SetGlobalBuffer(_latiosTextBuffer, persistentBuffer); + Shader.SetGlobalBuffer(_latiosTextBuffer, persistentGlyphBuffer); + + var persistentMaskBuffer = graphicsBroker.GetPersistentBuffer(kGlyphMasksBufferID, + math.max(worldBlackboardEntity.GetComponentData().maskCount, 128)); + + if (glyphsWithChildrenCount > 0) + { + m_uploadMasksShader.SetBuffer(0, _dst, persistentMaskBuffer); + m_uploadMasksShader.SetBuffer(0, _src, maskUploadBuffer); + m_uploadMasksShader.SetBuffer(0, _meta, maskMetaBuffer); + + for (uint dispatchesRemaining = (uint)maskPayloads.Length, offset = 0; dispatchesRemaining > 0;) + { + uint dispatchCount = math.min(dispatchesRemaining, 65535); + m_uploadMasksShader.SetInt(_startOffset, (int)offset); + m_uploadMasksShader.Dispatch(0, (int)dispatchCount, 1, 1); + offset += dispatchCount; + dispatchesRemaining -= dispatchCount; + } + + Shader.SetGlobalBuffer(_latiosTextMaskBuffer, persistentMaskBuffer); + } yield return true; } @@ -177,25 +314,20 @@ unsafe struct UploadPayload public uint controls; } - // Schedule Single + // Schedule Parallel [BurstCompile] - struct GatherUploadOperationsJob : IJobChunk + struct UpdateMaterialMasksJob : IJobChunk { [ReadOnly] public ComponentTypeHandle perFrameMaskHandle; - [ReadOnly] public ComponentTypeHandle trcHandle; [ReadOnly] public BufferTypeHandle glyphsHandle; + [ReadOnly] public BufferTypeHandle glyphMasksHandle; public ComponentTypeHandle perCameraMaskHandle; public ComponentTypeHandle materialPropertyDirtyMaskHandle; - public ComponentTypeHandle textShaderIndexHandle; - public ComponentLookup glyphCountThisFrameLookup; - public Entity worldBlackboardEntity; - - public uint glyphCountThisPass; - public ulong materialMaskLower; - public ulong materialMaskUpper; - - [NativeDisableParallelForRestriction] public NativeStream.Writer streamWriter; + public ulong glyphMaterialMaskLower; + public ulong glyphMaterialMaskUpper; + public ulong maskMaterialMaskLower; + public ulong maskMaterialMaskUpper; public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { @@ -206,34 +338,185 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo if ((upper | lower) == 0) return; - ref var dirtyMask = ref chunk.GetChunkComponentRefRW(ref materialPropertyDirtyMaskHandle); - dirtyMask.lower.Value |= materialMaskLower; - dirtyMask.upper.Value |= materialMaskUpper; + ref var dirtyMask = ref chunk.GetChunkComponentRefRW(ref materialPropertyDirtyMaskHandle); + + var glyphMasksBuffers = chunk.GetBufferAccessor(ref glyphMasksHandle); + if (glyphMasksBuffers.Length > 0) + { + var enumerator = new ChunkEntityEnumerator(true, new v128(lower, upper), chunk.Count); + while (enumerator.NextEntityIndex(out int i)) + { + var buffer = glyphMasksBuffers[i]; + if (buffer.Length == 0) + { + ref var bitHolder = ref i >= 64 ? ref cameraMask.upper : ref cameraMask.lower; + bitHolder.SetBits(i % 64, false); + } + } + if ((cameraMask.upper.Value | cameraMask.lower.Value) != 0) + { + dirtyMask.lower.Value |= maskMaterialMaskLower; + dirtyMask.upper.Value |= maskMaterialMaskUpper; + dirtyMask.lower.Value |= glyphMaterialMaskLower; + dirtyMask.upper.Value |= glyphMaterialMaskUpper; + } + } + else + { + var glyphsBuffers = chunk.GetBufferAccessor(ref glyphsHandle); + var enumerator = new ChunkEntityEnumerator(true, new v128(lower, upper), chunk.Count); + while (enumerator.NextEntityIndex(out int i)) + { + var buffer = glyphsBuffers[i]; + if (buffer.Length == 0) + { + ref var bitHolder = ref i >= 64 ? ref cameraMask.upper : ref cameraMask.lower; + bitHolder.SetBits(i % 64, false); + } + } + if ((cameraMask.upper.Value | cameraMask.lower.Value) != 0) + { + dirtyMask.lower.Value |= glyphMaterialMaskLower; + dirtyMask.upper.Value |= glyphMaterialMaskUpper; + } + } + } + } + + // Schedule Parallel + [BurstCompile] + struct FindCulledGlyphHoldersWithVisibleChildrenJob : IJobChunk + { + [ReadOnly] public ComponentTypeHandle perFrameMaskHandle; + [ReadOnly] public BufferTypeHandle additionalEntitiesHandle; + [ReadOnly] public ComponentTypeHandle perCameraMaskHandle; + [ReadOnly] public EntityStorageInfoLookup esil; + + public NativeParallelHashMap.ParallelWriter map; + + 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); + upper = ~upper; + lower = ~lower; + BitField64 lowerMask = default; + lowerMask.SetBits(0, true, math.min(chunk.Count, 64)); + BitField64 upperMask = default; + if (chunk.Count > 64) + upperMask.SetBits(0, true, chunk.Count - 64); + upper &= upperMask.Value; + lower &= lowerMask.Value; + + if ((upper | lower) == 0) + return; + + var entitiesBuffers = chunk.GetBufferAccessor(ref additionalEntitiesHandle); + var enumerator = new ChunkEntityEnumerator(true, new v128(lower, upper), chunk.Count); + while (enumerator.NextEntityIndex(out int i)) + { + bool survive = false; + foreach (var entity in entitiesBuffers[i]) + { + var info = esil[entity.entity]; + var childCameraMask = chunk.GetChunkComponentData(ref perCameraMaskHandle); + var childFrameMask = chunk.GetChunkComponentData(ref perFrameMaskHandle); + if (info.IndexInChunk >= 64) + { + if (!childFrameMask.upper.IsSet(info.IndexInChunk - 64) && childCameraMask.upper.IsSet(info.IndexInChunk - 64)) + { + survive = true; + break; + } + } + else + { + if (!childFrameMask.lower.IsSet(info.IndexInChunk) && childCameraMask.lower.IsSet(info.IndexInChunk)) + { + survive = true; + break; + } + } + } + if (!survive) + { + if (i >= 64) + upper ^= 1u << (i - 64); + else + lower ^= 1u << i; + } + } + if ((upper | lower) != 0) + { + map.TryAdd(chunk, new v128(lower, upper)); + } + } + } + + // Schedule Single + [BurstCompile] + struct GatherGlyphUploadOperationsJob : IJobChunk + { + [ReadOnly] public ComponentTypeHandle perFrameMaskHandle; + [ReadOnly] public ComponentTypeHandle trcHandle; + [ReadOnly] public BufferTypeHandle glyphsHandle; + [ReadOnly] public BufferTypeHandle glyphMaskHandle; + [ReadOnly] public ComponentTypeHandle perCameraMaskHandle; + [ReadOnly] public BufferTypeHandle additonalEntitiesHandle; + [ReadOnly] public NativeParallelHashMap map; + public ComponentTypeHandle textShaderIndexHandle; + public ComponentLookup glyphCountThisFrameLookup; + public Entity worldBlackboardEntity; + + public uint glyphCountThisPass; + + [NativeDisableParallelForRestriction] public NativeStream.Writer streamWriter; + + 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); + + if (chunk.Has(ref additonalEntitiesHandle)) + { + // If any child is being rendered, we still need to upload glyphs. + if (map.TryGetValue(chunk, out var extraBits)) + { + lower |= extraBits.ULong0; + upper |= extraBits.ULong1; + } + } + if ((upper | lower) == 0) + return; ref var glyphCountThisFrame = ref glyphCountThisFrameLookup.GetRefRW(worldBlackboardEntity).ValueRW.glyphCount; streamWriter.BeginForEachIndex(unfilteredChunkIndex); var trcs = chunk.GetNativeArray(ref trcHandle); var glyphsBuffers = chunk.GetBufferAccessor(ref glyphsHandle); + var masksBuffers = chunk.GetBufferAccessor(ref glyphMaskHandle); var shaderIndices = chunk.GetNativeArray(ref textShaderIndexHandle); var enumerator = new ChunkEntityEnumerator(true, new v128(lower, upper), chunk.Count); while (enumerator.NextEntityIndex(out int i)) { - var trc = trcs[i].flags; - var buffer = glyphsBuffers[i]; + var trc = trcs[i].flags; + var buffer = glyphsBuffers[i]; + int glyphCount = buffer.Length; + if (masksBuffers.Length > 0) + { + glyphCount = masksBuffers[i].Length * 16; + } shaderIndices[i] = new TextShaderIndex { firstGlyphIndex = glyphCountThisFrame, - glyphCount = (uint)buffer.Length + glyphCount = (uint)glyphCount }; - if (buffer.Length == 0) - { - ref var bitHolder = ref i >= 64 ? ref cameraMask.upper : ref cameraMask.lower; - bitHolder.SetBits(i % 64, false); - } - streamWriter.Write(new UploadPayload { ptr = buffer.GetUnsafeReadOnlyPtr(), @@ -250,6 +533,94 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo } } + // Schedule Single + [BurstCompile] + struct GatherMaskUploadOperationsJob : IJobChunk + { + [ReadOnly] public ComponentTypeHandle perFrameMaskHandle; + [ReadOnly] public BufferTypeHandle glyphMasksHandle; + [ReadOnly] public ComponentTypeHandle perCameraMaskHandle; + public ComponentTypeHandle maskShaderIndexHandle; + public ComponentLookup maskCountThisFrameLookup; + public Entity worldBlackboardEntity; + + public uint maskCountThisPass; + + [NativeDisableParallelForRestriction] public NativeStream.Writer streamWriter; + + public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) + { + ref var cameraMask = ref chunk.GetChunkComponentRefRW(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; + + ref var maskCountThisFrame = ref maskCountThisFrameLookup.GetRefRW(worldBlackboardEntity).ValueRW.maskCount; + + streamWriter.BeginForEachIndex(unfilteredChunkIndex); + var glyphMasksBuffers = chunk.GetBufferAccessor(ref glyphMasksHandle); + var shaderIndices = chunk.GetNativeArray(ref maskShaderIndexHandle); + + var enumerator = new ChunkEntityEnumerator(true, new v128(lower, upper), chunk.Count); + while (enumerator.NextEntityIndex(out int i)) + { + var buffer = glyphMasksBuffers[i]; + shaderIndices[i] = new TextMaterialMaskShaderIndex + { + firstMaskIndex = maskCountThisFrame + }; + + streamWriter.Write(new UploadPayload + { + ptr = buffer.GetUnsafeReadOnlyPtr(), + length = (uint)buffer.Length, + uploadBufferStart = maskCountThisPass, + persistentBufferStart = maskCountThisFrame, + controls = 0 + }); + maskCountThisPass += (uint)buffer.Length; + maskCountThisFrame += (uint)buffer.Length; + } + + streamWriter.EndForEachIndex(); + } + } + + // Schedule Parallel + [BurstCompile] + struct CopyGlyphShaderIndicesJob : IJobChunk + { + [ReadOnly] public ComponentTypeHandle perFrameMaskHandle; + [ReadOnly] public ComponentTypeHandle perCameraMaskHandle; + [ReadOnly] public ComponentTypeHandle shaderIndexHandle; + [ReadOnly] public BufferTypeHandle additionalEntitiesHandle; + [NativeDisableContainerSafetyRestriction] public ComponentLookup shaderIndexLookup; + + public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) + { + ref var cameraMask = ref chunk.GetChunkComponentRefRW(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; + + var additionalEntitiesBuffers = chunk.GetBufferAccessor(ref additionalEntitiesHandle); + var shaderIndices = chunk.GetNativeArray(ref shaderIndexHandle); + + var enumerator = new ChunkEntityEnumerator(true, new v128(lower, upper), chunk.Count); + while (enumerator.NextEntityIndex(out int i)) + { + foreach (var entity in additionalEntitiesBuffers[i]) + { + shaderIndexLookup[entity.entity] = shaderIndices[i]; + } + } + } + } + [BurstCompile] struct MapPayloadsToUploadBufferJob : IJob { @@ -279,8 +650,9 @@ public void Execute() } } + // Schedule Parallel [BurstCompile] - struct WriteUploadsToBuffersJob : IJobParallelForDefer + struct WriteGlyphsUploadsToBuffersJob : IJobParallelForDefer { [ReadOnly] public NativeArray payloads; public NativeArray metaUploadBuffer; @@ -294,6 +666,23 @@ public unsafe void Execute(int index) UnsafeUtility.MemCpy(dstPtr, payload.ptr, sizeof(RenderGlyph) * payload.length); } } + + // Schedule Parallel + [BurstCompile] + struct WriteMasksUploadsToBuffersJob : IJobParallelForDefer + { + [ReadOnly] public NativeArray payloads; + public NativeArray metaUploadBuffer; + [NativeDisableParallelForRestriction] public NativeArray masksUploadBuffer; + + public unsafe void Execute(int index) + { + var payload = payloads[index]; + metaUploadBuffer[index] = new uint3(payload.uploadBufferStart, payload.persistentBufferStart, payload.length); + var dstPtr = masksUploadBuffer.GetSubArray((int)payload.uploadBufferStart, (int)payload.length).GetUnsafePtr(); + UnsafeUtility.MemCpy(dstPtr, payload.ptr, sizeof(uint) * payload.length); + } + } } } diff --git a/Calligraphics/Systems/Rendering/TextRenderingUpdateSystem.cs b/Calligraphics/Systems/Rendering/TextRenderingUpdateSystem.cs index 34190da..9df275d 100644 --- a/Calligraphics/Systems/Rendering/TextRenderingUpdateSystem.cs +++ b/Calligraphics/Systems/Rendering/TextRenderingUpdateSystem.cs @@ -20,7 +20,8 @@ public partial struct TextRenderingUpdateSystem : ISystem { LatiosWorldUnmanaged latiosWorld; - EntityQuery m_query; + EntityQuery m_singleFontQuery; + EntityQuery m_multiFontQuery; bool m_skipChangeFilter; [BurstCompile] @@ -28,9 +29,11 @@ public void OnCreate(ref SystemState state) { latiosWorld = state.GetLatiosWorldUnmanaged(); - m_query = state.Fluent().With(true).With().With().With().Build(); + m_singleFontQuery = state.Fluent().With(true).With().Without().Build(); + m_multiFontQuery = state.Fluent().With(true).With().Build(); latiosWorld.worldBlackboardEntity.AddComponent(); + latiosWorld.worldBlackboardEntity.AddComponent(); m_skipChangeFilter = (state.WorldUnmanaged.Flags & WorldFlags.Editor) == WorldFlags.Editor; } @@ -43,19 +46,33 @@ public void OnDestroy(ref SystemState state) public void OnUpdate(ref SystemState state) { latiosWorld.worldBlackboardEntity.SetComponentData(new GlyphCountThisFrame { glyphCount = 0 }); + latiosWorld.worldBlackboardEntity.SetComponentData(new MaskCountThisFrame { maskCount = 1 }); // Zero reserved for no mask - state.Dependency = new Job + state.Dependency = new SingleFontJob { glyphHandle = GetBufferTypeHandle(true), boundsHandle = GetComponentTypeHandle(false), controlHandle = GetComponentTypeHandle(false), materialMeshInfoHandle = GetComponentTypeHandle(false), lastSystemVersion = m_skipChangeFilter ? 0 : state.LastSystemVersion - }.ScheduleParallel(m_query, state.Dependency); + }.ScheduleParallel(m_singleFontQuery, state.Dependency); + + state.Dependency = new MultiFontJob + { + additionalEntityHandle = GetBufferTypeHandle(true), + boundsLookup = GetComponentLookup(false), + controlLookup = GetComponentLookup(false), + entityHandle = GetEntityTypeHandle(), + glyphHandle = GetBufferTypeHandle(true), + glyphMaskLookup = GetBufferLookup(false), + lastSystemVersion = m_skipChangeFilter ? 0 : state.LastSystemVersion, + materialMeshInfoLookup = GetComponentLookup(false), + selectorHandle = GetBufferTypeHandle(true) + }.ScheduleParallel(m_multiFontQuery, state.Dependency); } [BurstCompile] - struct Job : IJobChunk + struct SingleFontJob : IJobChunk { [ReadOnly] public BufferTypeHandle glyphHandle; public ComponentTypeHandle boundsHandle; @@ -65,7 +82,7 @@ struct Job : IJobChunk public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { - if (!(chunk.DidChange(ref glyphHandle, lastSystemVersion) || chunk.DidChange(ref controlHandle, lastSystemVersion))) + if (!chunk.DidChange(ref controlHandle, lastSystemVersion)) return; var ctrlRO = chunk.GetComponentDataPtrRO(ref controlHandle); @@ -127,6 +144,133 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo } } } + + [BurstCompile] + struct MultiFontJob : IJobChunk + { + [ReadOnly] public EntityTypeHandle entityHandle; + [ReadOnly] public BufferTypeHandle glyphHandle; + [ReadOnly] public BufferTypeHandle selectorHandle; + [ReadOnly] public BufferTypeHandle additionalEntityHandle; + [NativeDisableParallelForRestriction] public ComponentLookup boundsLookup; + [NativeDisableParallelForRestriction] public ComponentLookup controlLookup; + [NativeDisableParallelForRestriction] public ComponentLookup materialMeshInfoLookup; + [NativeDisableParallelForRestriction] public BufferLookup glyphMaskLookup; + public uint lastSystemVersion; + + public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) + { + var entities = chunk.GetNativeArray(entityHandle); + if (!controlLookup.DidChange(entities[0], lastSystemVersion)) + return; + + var glyphBuffers = chunk.GetBufferAccessor(ref glyphHandle); + var selectorBuffers = chunk.GetBufferAccessor(ref selectorHandle); + var entityBuffers = chunk.GetBufferAccessor(ref additionalEntityHandle); + + FixedList4096Bytes instances = default; + + for (int entityIndex = 0; entityIndex < chunk.Count; entityIndex++) + { + var entity = entities[entityIndex]; + + var ctrl = controlLookup[entity]; + if ((ctrl.flags & TextRenderControl.Flags.Dirty) != TextRenderControl.Flags.Dirty) + continue; + ctrl.flags &= ~TextRenderControl.Flags.Dirty; + + var glyphBuffer = glyphBuffers[entityIndex].AsNativeArray(); + var selectorBuffer = selectorBuffers[entityIndex].AsNativeArray().Reinterpret(); + var entityBuffer = entityBuffers[entityIndex].AsNativeArray().Reinterpret(); + + instances.Add(new FontMaterialInstance + { + masks = glyphMaskLookup[entity].Reinterpret(), + aabb = new Aabb(float.MaxValue, float.MinValue), + entity = entity + }); + + for (int i = 0; i < entityBuffer.Length; i++) + { + instances.Add(new FontMaterialInstance + { + masks = glyphMaskLookup[entityBuffer[i]].Reinterpret(), + aabb = new Aabb(float.MaxValue, float.MinValue), + entity = entityBuffer[i] + }); + } + + var glyphCount = math.min(glyphBuffer.Length, selectorBuffer.Length); + for (int i = 0; i < glyphCount; i++) + { + var glyph = glyphBuffer[i]; + var c = (glyph.blPosition + glyph.trPosition) / 2f; + var e = math.length(c - glyph.blPosition); + e += glyph.shear; + + var selectIndex = selectorBuffer[i]; + ref var instance = ref instances.ElementAt(selectIndex); + + instance.aabb = Physics.CombineAabb(instance.aabb, new Aabb(new float3(c - e, 0f), new float3(c + e, 0f))); + + if (instance.masks.Length > 0) + { + ref var lastMask = ref instance.masks.ElementAt(instance.masks.Length - 1); + var offset = lastMask & 0xffff; + if (i - offset < 16) + { + var bit = i - offset + 16; + lastMask |= 1u << (byte)bit; + continue; + } + } + instance.masks.Add((uint)i + 0x10000); + } + + for (int i = 0; i < instances.Length; i++) + { + ref var instance = ref instances.ElementAt(i); + + Physics.GetCenterExtents(instance.aabb, out var center, out var extents); + if (glyphBuffer.Length == 0) + { + center = 0f; + extents = 0f; + } + boundsLookup[instance.entity] = new RenderBounds { Value = new AABB { Center = center, Extents = extents } }; + controlLookup[instance.entity] = ctrl; + ref var mmi = + ref materialMeshInfoLookup.GetRefRW(instance.entity).ValueRW; + + var quadCount = instance.masks.Length * 16; + if (quadCount == 16) + quadCount = math.countbits(instance.masks[0] & 0xffff0000); + if (quadCount <= 8) + mmi.SubMesh = 0; + else if (glyphBuffer.Length <= 64) + mmi.SubMesh = 1; + else if (glyphBuffer.Length <= 512) + mmi.SubMesh = 2; + else if (glyphBuffer.Length <= 4096) + mmi.SubMesh = 3; + else if (glyphBuffer.Length <= 16384) + mmi.SubMesh = 4; + else + { + UnityEngine.Debug.LogWarning("Glyphs in RenderGlyph buffer exceeds max capacity of 16384 and will be truncated."); + mmi.SubMesh = 4; + } + } + } + } + + struct FontMaterialInstance + { + public DynamicBuffer masks; + public Aabb aabb; + public Entity entity; + } + } } } diff --git a/Core/GameplayToolkit/DynamicHashMap.cs b/Core/GameplayToolkit/DynamicHashMap.cs index 704281d..f528ed1 100644 --- a/Core/GameplayToolkit/DynamicHashMap.cs +++ b/Core/GameplayToolkit/DynamicHashMap.cs @@ -228,7 +228,7 @@ public struct Pair internal bool isOccupied { - get => (meta & 0x10000000) != 0; + get => (meta & 0x80000000) != 0; set => meta = (meta & 0x7fffffff) | math.select(0u, 1u, value) << 31; } diff --git a/Core/Utilities/BlobBuilderExtensions.cs b/Core/Utilities/BlobBuilderExtensions.cs index cea2d3c..130a205 100644 --- a/Core/Utilities/BlobBuilderExtensions.cs +++ b/Core/Utilities/BlobBuilderExtensions.cs @@ -1,4 +1,5 @@ -using Unity.Collections; +using System; +using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Entities; @@ -22,6 +23,12 @@ unsafe public static void AllocateFixedString(ref this BlobBuilder builder, r res[i] = fixedString[i]; } } + + // Todo: Find a new home for this? + public unsafe static ReadOnlySpan AsSpan(ref this BlobArray blobArray) where T : unmanaged + { + return new ReadOnlySpan(blobArray.GetUnsafePtr(), blobArray.Length); + } } } diff --git a/Kinemation/ACL_Unity/AclUnityDecompression.cs b/Kinemation/ACL_Unity/AclUnityDecompression.cs index 16a11ae..28efef9 100644 --- a/Kinemation/ACL_Unity/AclUnityDecompression.cs +++ b/Kinemation/ACL_Unity/AclUnityDecompression.cs @@ -20,7 +20,6 @@ public enum KeyframeInterpolationMode : byte Nearest = 3 } - // Warning: If you do not provide enough elements to outputBuffer, this may cause data corruption or even hard crash public static void SamplePose(void* compressedTransformsClip, NativeArray outputBuffer, float time, KeyframeInterpolationMode keyframeInterpolationMode) { CheckCompressedClipIsValid(compressedTransformsClip); @@ -48,7 +47,6 @@ public static void SamplePose(void* compressedTransformsClip, NativeArray } } - // Warning: If you do not provide enough elements to outputBuffer, this may cause data corruption or even hard crash public static void SamplePoseBlendedFirst(void* compressedTransformsClip, NativeArray outputBuffer, float blendFactor, @@ -121,6 +119,141 @@ public static void SamplePoseBlendedAdd(void* compressedTran } } + public static void SamplePoseMasked(void* compressedTransformsClip, + NativeArray outputBuffer, + ReadOnlySpan mask, + float time, + KeyframeInterpolationMode keyframeInterpolationMode) + { + CheckCompressedClipIsValid(compressedTransformsClip); + + var header = ClipHeader.Read(compressedTransformsClip); + + if (header.clipType == ClipHeader.ClipType.Scalars) + { + ThrowIfWrongType(); + } + + CheckOutputArrayIsSufficient(outputBuffer, header.trackCount); + CheckMaskSpanIsSufficient(mask, header.trackCount); + + compressedTransformsClip = (byte*)compressedTransformsClip + 16; + void* compressedScalesClip = header.clipType == + ClipHeader.ClipType.SkeletonWithUniformScales ? (byte*)compressedTransformsClip + header.offsetToUniformScalesStartInBytes : null; + fixed (ulong* maskPtr = &mask[0]) + { + if (X86.Avx2.IsAvx2Supported) + { + AVX.samplePoseMasked(compressedTransformsClip, compressedScalesClip, (float*)outputBuffer.GetUnsafePtr(), maskPtr, time, (byte)keyframeInterpolationMode); + } + else + { + NoExtensions.samplePoseMasked(compressedTransformsClip, + compressedScalesClip, + (float*)outputBuffer.GetUnsafePtr(), + maskPtr, + time, + (byte)keyframeInterpolationMode); + } + } + } + + public static void SamplePoseMaskedBlendedFirst(void* compressedTransformsClip, + NativeArray outputBuffer, + ReadOnlySpan mask, + float blendFactor, + float time, + KeyframeInterpolationMode keyframeInterpolationMode) + { + CheckCompressedClipIsValid(compressedTransformsClip); + + var header = ClipHeader.Read(compressedTransformsClip); + + if (header.clipType == ClipHeader.ClipType.Scalars) + { + ThrowIfWrongType(); + } + + CheckOutputArrayIsSufficient(outputBuffer, header.trackCount); + CheckMaskSpanIsSufficient(mask, header.trackCount); + + compressedTransformsClip = (byte*)compressedTransformsClip + 16; + void* compressedScalesClip = header.clipType == + ClipHeader.ClipType.SkeletonWithUniformScales ? (byte*)compressedTransformsClip + header.offsetToUniformScalesStartInBytes : null; + + fixed (ulong* maskPtr = &mask[0]) + { + if (X86.Avx2.IsAvx2Supported) + { + AVX.samplePoseMaskedBlendedFirst(compressedTransformsClip, + compressedScalesClip, + (float*)outputBuffer.GetUnsafePtr(), + maskPtr, + blendFactor, + time, + (byte)keyframeInterpolationMode); + } + else + { + NoExtensions.samplePoseMaskedBlendedFirst(compressedTransformsClip, + compressedScalesClip, + (float*)outputBuffer.GetUnsafePtr(), + maskPtr, + blendFactor, + time, + (byte)keyframeInterpolationMode); + } + } + } + + public static void SamplePoseMaskedBlendedAdd(void* compressedTransformsClip, + NativeArray outputBuffer, + ReadOnlySpan mask, + float blendFactor, + float time, + KeyframeInterpolationMode keyframeInterpolationMode) + { + CheckCompressedClipIsValid(compressedTransformsClip); + + var header = ClipHeader.Read(compressedTransformsClip); + + if (header.clipType == ClipHeader.ClipType.Scalars) + { + ThrowIfWrongType(); + } + + CheckOutputArrayIsSufficient(outputBuffer, header.trackCount); + CheckMaskSpanIsSufficient(mask, header.trackCount); + + compressedTransformsClip = (byte*)compressedTransformsClip + 16; + void* compressedScalesClip = header.clipType == + ClipHeader.ClipType.SkeletonWithUniformScales ? (byte*)compressedTransformsClip + header.offsetToUniformScalesStartInBytes : null; + + fixed (ulong* maskPtr = &mask[0]) + { + if (X86.Avx2.IsAvx2Supported) + { + AVX.samplePoseMaskedBlendedAdd(compressedTransformsClip, + compressedScalesClip, + (float*)outputBuffer.GetUnsafePtr(), + maskPtr, + blendFactor, + time, + (byte)keyframeInterpolationMode); + } + else + { + NoExtensions.samplePoseMaskedBlendedAdd(compressedTransformsClip, + compressedScalesClip, + (float*)outputBuffer.GetUnsafePtr(), + maskPtr, + blendFactor, + time, + (byte)keyframeInterpolationMode); + } + } + } + public static Qvvs SampleBone(void* compressedTransformsClip, int boneIndex, float time, KeyframeInterpolationMode keyframeInterpolationMode) { CheckCompressedClipIsValid(compressedTransformsClip); @@ -152,7 +285,6 @@ public static Qvvs SampleBone(void* compressedTransformsClip, int boneIndex, flo return qvv; } - // Warning: If you do not provide enough elements to outputBuffer, this may cause data corruption or even hard crash public static void SampleFloats(void* compressedFloatsClip, NativeArray outputBuffer, float time, KeyframeInterpolationMode keyframeInterpolationMode) { CheckCompressedClipIsValid(compressedFloatsClip); @@ -178,6 +310,39 @@ public static void SampleFloats(void* compressedFloatsClip, NativeArray o } } + public static void SampleFloatsMasked(void* compressedFloatsClip, + NativeArray outputBuffer, + ReadOnlySpan mask, + float time, + KeyframeInterpolationMode keyframeInterpolationMode) + { + CheckCompressedClipIsValid(compressedFloatsClip); + + var header = ClipHeader.Read(compressedFloatsClip); + + if (header.clipType != ClipHeader.ClipType.Scalars) + { + ThrowIfWrongType(); + } + + CheckOutputArrayIsSufficient(outputBuffer, header.trackCount); + CheckMaskSpanIsSufficient(mask, header.trackCount); + + compressedFloatsClip = (byte*)compressedFloatsClip + 16; + + fixed (ulong* maskPtr = &mask[0]) + { + if (X86.Avx2.IsAvx2Supported) + { + AVX.sampleFloatsMasked(compressedFloatsClip, (float*)outputBuffer.GetUnsafePtr(), maskPtr, time, (byte)keyframeInterpolationMode); + } + else + { + NoExtensions.sampleFloatsMasked(compressedFloatsClip, (float*)outputBuffer.GetUnsafePtr(), maskPtr, time, (byte)keyframeInterpolationMode); + } + } + } + public static float SampleFloat(void* compressedFloatsClip, int trackIndex, float time, KeyframeInterpolationMode keyframeInterpolationMode) { CheckCompressedClipIsValid(compressedFloatsClip); @@ -236,6 +401,15 @@ static void CheckOutputArrayIsSufficient(NativeArray outputBuffer, short throw new ArgumentException("outputBuffer does not contain enough elements"); } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] + static void CheckMaskSpanIsSufficient(ReadOnlySpan mask, short trackCount) + { + if (mask.IsEmpty) + throw new ArgumentException("mask is invalid"); + if (mask.Length * 64 < trackCount) + throw new ArgumentException("mask does not contain enough elements"); + } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] static void CheckIndexIsValid(int index, short trackCount) { diff --git a/Kinemation/ACL_Unity/Plugins/OSX/AArch64/AclUnity.dylib b/Kinemation/ACL_Unity/Plugins/OSX/AArch64/AclUnity.dylib index 2a5f2b0..6070e1e 100644 Binary files a/Kinemation/ACL_Unity/Plugins/OSX/AArch64/AclUnity.dylib and b/Kinemation/ACL_Unity/Plugins/OSX/AArch64/AclUnity.dylib differ diff --git a/Kinemation/ACL_Unity/Plugins/OSX/X86/libAclUnity.dylib b/Kinemation/ACL_Unity/Plugins/OSX/X86/libAclUnity.dylib index 650ca81..191dd03 100644 Binary files a/Kinemation/ACL_Unity/Plugins/OSX/X86/libAclUnity.dylib and b/Kinemation/ACL_Unity/Plugins/OSX/X86/libAclUnity.dylib differ diff --git a/Kinemation/ACL_Unity/Plugins/OSX/X86/libAclUnity_AVX.dylib b/Kinemation/ACL_Unity/Plugins/OSX/X86/libAclUnity_AVX.dylib index f1b3007..e51132b 100644 Binary files a/Kinemation/ACL_Unity/Plugins/OSX/X86/libAclUnity_AVX.dylib and b/Kinemation/ACL_Unity/Plugins/OSX/X86/libAclUnity_AVX.dylib differ diff --git a/Kinemation/Components/AnimationComponents.cs b/Kinemation/Components/AnimationComponents.cs index 0a3a679..a847c47 100644 --- a/Kinemation/Components/AnimationComponents.cs +++ b/Kinemation/Components/AnimationComponents.cs @@ -124,7 +124,7 @@ public unsafe TransformQvvs SampleBone(int boneIndex, float time, KeyframeInterp /// Samples the animation clip for the entire skeleton at the given time weighted by the blendWeight. /// This method uses a special fast-path. /// - /// The blender which contains context information about previous sampling operations + /// The skeleton aspect on which to apply the animation /// The time value to sample the the clip in seconds. /// A weight factor to use for blending in the range of (0f, 1f] /// This value is automatically clamped to a value between 0f and the clip's duration. @@ -140,12 +140,36 @@ public unsafe void SamplePose(ref OptimizedSkeletonAspect optimizedSkeletonAspec if (optimizedSkeletonAspect.BeginSampleTrueIfAdditive(out var buffer)) AclUnity.Decompression.SamplePoseBlendedAdd(compressedClipDataAligned16.GetUnsafePtr(), buffer, blendWeight, time, mode); - else if (blendWeight == 1f) - AclUnity.Decompression.SamplePose(compressedClipDataAligned16.GetUnsafePtr(), buffer, time, mode); else AclUnity.Decompression.SamplePoseBlendedFirst(compressedClipDataAligned16.GetUnsafePtr(), buffer, blendWeight, time, mode); } + /// + /// Samples the animation clip for parts of the skeleton specified by the mask at the given time weighted by the blendWeight. + /// This method uses a special fast-path. + /// + /// The skeleton aspect on which to apply the animation + /// A bit array where each bit specifies if the bone at that index should be sampled + /// The time value to sample the the clip in seconds. + /// A weight factor to use for blending in the range of (0f, 1f] + /// This value is automatically clamped to a value between 0f and the clip's duration. + /// The mechanism used to sample a time value between two keyframes + public unsafe void SamplePose(ref OptimizedSkeletonAspect optimizedSkeletonAspect, + ReadOnlySpan mask, + float time, + float blendWeight, + KeyframeInterpolationMode keyframeInterpolationMode = KeyframeInterpolationMode.Interpolate) + { + CheckSkeletonIsBigEnoughForClip(in optimizedSkeletonAspect, boneCount); + + var mode = (AclUnity.Decompression.KeyframeInterpolationMode)keyframeInterpolationMode; + + if (optimizedSkeletonAspect.BeginSampleTrueIfAdditive(out var buffer)) + AclUnity.Decompression.SamplePoseMaskedBlendedAdd(compressedClipDataAligned16.GetUnsafePtr(), buffer, mask, blendWeight, time, mode); + else + AclUnity.Decompression.SamplePoseMaskedBlendedFirst(compressedClipDataAligned16.GetUnsafePtr(), buffer, mask, blendWeight, time, mode); + } + /// /// Samples the animation clip for the entire set of transforms at the given time weighted by the blendWeight. /// This method uses a special fast-path. @@ -166,18 +190,115 @@ public unsafe void SamplePose(ref BufferPoseBlender blender, if (blender.sampledFirst) AclUnity.Decompression.SamplePoseBlendedAdd(compressedClipDataAligned16.GetUnsafePtr(), blender.bufferAsQvvs, blendWeight, time, mode); - else if (blendWeight == 1f) + else { - AclUnity.Decompression.SamplePose(compressedClipDataAligned16.GetUnsafePtr(), blender.bufferAsQvvs, time, mode); + AclUnity.Decompression.SamplePoseBlendedFirst(compressedClipDataAligned16.GetUnsafePtr(), blender.bufferAsQvvs, blendWeight, time, mode); blender.sampledFirst = true; } + } + + /// + /// Samples the animation clip for the transforms selected by the mask at the given time weighted by the blendWeight. + /// This method uses a special fast-path. + /// + /// The blender which contains context information about previous sampling operations + /// A bit array where each bit specifies if the bone at that index should be sampled + /// The time value to sample the the clip in seconds. + /// A weight factor to use for blending in the range of (0f, 1f] + /// This value is automatically clamped to a value between 0f and the clip's duration. + /// The mechanism used to sample a time value between two keyframes + public unsafe void SamplePose(ref BufferPoseBlender blender, + ReadOnlySpan mask, + float time, + float blendWeight = 1f, + KeyframeInterpolationMode keyframeInterpolationMode = KeyframeInterpolationMode.Interpolate) + { + CheckBlenderIsBigEnoughForClip(in blender, boneCount); + + var mode = (AclUnity.Decompression.KeyframeInterpolationMode)keyframeInterpolationMode; + + if (blender.sampledFirst) + AclUnity.Decompression.SamplePoseMaskedBlendedAdd(compressedClipDataAligned16.GetUnsafePtr(), blender.bufferAsQvvs, mask, blendWeight, time, mode); else { - AclUnity.Decompression.SamplePoseBlendedFirst(compressedClipDataAligned16.GetUnsafePtr(), blender.bufferAsQvvs, blendWeight, time, mode); + AclUnity.Decompression.SamplePoseMaskedBlendedFirst(compressedClipDataAligned16.GetUnsafePtr(), blender.bufferAsQvvs, mask, blendWeight, time, mode); blender.sampledFirst = true; } } + /// + /// Samples the animation clip for the entire set of transforms at the given time raw into a buffer without any blending. + /// worldIndex values are undefined. This method uses a special fast-path. + /// + /// The raw local transforms array to overwrite with the sampled data. + /// The time value to sample the the clip in seconds. + /// The mechanism used to sample a time value between two keyframes + public unsafe void SamplePose(ref NativeArray localTransforms, + float time, + KeyframeInterpolationMode keyframeInterpolationMode = KeyframeInterpolationMode.Interpolate) + { + var mode = (AclUnity.Decompression.KeyframeInterpolationMode)keyframeInterpolationMode; + AclUnity.Decompression.SamplePose(compressedClipDataAligned16.GetUnsafePtr(), localTransforms.Reinterpret(), time, mode); + } + + /// + /// Samples the animation clip for the entire set of transforms at the given time raw into a buffer with blending. + /// New transforms can be specified to either overwrite existing values or be added via weighting. This method uses a special fast-path. + /// + /// The raw local transforms array to overwrite with the sampled data. + /// The time value to sample the the clip in seconds. + /// A weight factor to use for blending in the range of (0f, 1f] + /// If true, the existing transforms and accumulated weights are overwritten. + /// If false, the new transform values are added to the existing values. + /// The mechanism used to sample a time value between two keyframes + public unsafe void SamplePose(ref NativeArray localTransforms, + float time, + float blendWeight, + bool overwrite, + KeyframeInterpolationMode keyframeInterpolationMode = KeyframeInterpolationMode.Interpolate) + { + var mode = (AclUnity.Decompression.KeyframeInterpolationMode)keyframeInterpolationMode; + if (overwrite) + AclUnity.Decompression.SamplePoseBlendedFirst(compressedClipDataAligned16.GetUnsafePtr(), localTransforms.Reinterpret(), blendWeight, time, mode); + else + AclUnity.Decompression.SamplePoseBlendedAdd(compressedClipDataAligned16.GetUnsafePtr(), localTransforms.Reinterpret(), blendWeight, time, mode); + } + + /// + /// Samples the animation clip for the transforms selected by the mask at the given time raw into a buffer with blending. + /// New transforms can be specified to either overwrite existing values or be added via weighting. This method uses a special fast-path. + /// + /// The raw local transforms array to overwrite with the sampled data. + /// A bit array where each bit specifies if the bone at that index should be sampled + /// The time value to sample the the clip in seconds. + /// A weight factor to use for blending in the range of (0f, 1f] + /// If true, the existing transforms and accumulated weights are overwritten. + /// If false, the new transform values are added to the existing values. + /// The mechanism used to sample a time value between two keyframes + public unsafe void SamplePose(ref NativeArray localTransforms, + ReadOnlySpan mask, + float time, + float blendWeight, + bool overwrite, + KeyframeInterpolationMode keyframeInterpolationMode = KeyframeInterpolationMode.Interpolate) + { + var mode = (AclUnity.Decompression.KeyframeInterpolationMode)keyframeInterpolationMode; + if (overwrite) + AclUnity.Decompression.SamplePoseMaskedBlendedFirst(compressedClipDataAligned16.GetUnsafePtr(), + localTransforms.Reinterpret(), + mask, + blendWeight, + time, + mode); + else + AclUnity.Decompression.SamplePoseMaskedBlendedAdd(compressedClipDataAligned16.GetUnsafePtr(), + localTransforms.Reinterpret(), + mask, + blendWeight, + time, + mode); + } + /// /// The size the clip would be if uncompressed, ignoring padding bytes /// @@ -212,7 +333,7 @@ public struct ParameterClipSetBlob /// /// Equivalent to the FixedString128Bytes.GetHashCode() for each parameter name /// - public BlobArray parameterNameHashes; + public BlobArray parameterNameHashes; public BlobArray parameterNames; } @@ -298,7 +419,7 @@ public unsafe float SampleParameter(int parameterIndex, float time, KeyframeInte } /// - /// Samples the animation clip for all parameters at the given time at once + /// Samples the animation clip for all parameters at the given time at once. This method uses a special fast-path. /// /// The array of floats where the parameters should be stored. If the array is not large enough, a safety exception is thrown. /// @@ -314,6 +435,26 @@ public unsafe void SampleAllParameters(NativeArray destination, AclUnity.Decompression.SampleFloats(compressedClipDataAligned16.GetUnsafePtr(), destination, time, mode); } + /// + /// Samples the animation clip for the selected set of parameters specified by the mask at the given time at once. + /// This method uses a special fast-path. + /// + /// The array of floats where the parameters should be stored. If the array is not large enough, a safety exception is thrown. + /// A bit array where each bit specifies if the parameter at that index should be sampled + /// + /// The time value to sample the the clip in seconds. + /// This value is automatically clamped to a value between 0f and the clip's duration. + /// The mechanism used to sample a time value between two keyframes + public unsafe void SampleSelectedParameters(NativeArray destination, + ReadOnlySpan mask, + float time, + KeyframeInterpolationMode keyframeInterpolationMode = KeyframeInterpolationMode.Interpolate) + { + CheckBufferIsBigEnoughForClip(destination, parameterCount); + var mode = (AclUnity.Decompression.KeyframeInterpolationMode)keyframeInterpolationMode; + AclUnity.Decompression.SampleFloatsMasked(compressedClipDataAligned16.GetUnsafePtr(), destination, mask, time, mode); + } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] void CheckBufferIsBigEnoughForClip(NativeArray buffer, short parameterCount) { diff --git a/Kinemation/Components/OptimizedSkeletonAspect.cs b/Kinemation/Components/OptimizedSkeletonAspect.cs index e0d7765..0585087 100644 --- a/Kinemation/Components/OptimizedSkeletonAspect.cs +++ b/Kinemation/Components/OptimizedSkeletonAspect.cs @@ -92,6 +92,8 @@ namespace Latios.Kinemation /// To ensure the contents are valid, call CopyFromPrevious() if isDirty was false. /// This method sets isDirty and needsSync to true. Root-space transforms are not automatically synced /// when you make changes to this array. You must call EndSamplingAndSync() when you are done making changes. + /// The worldIndex of each transform is treated as a float representing the accumulated weights of all samples. + /// Assign any transform's worldIndex with math.asint(1f) to disable normalization fo that transform during sync. /// If you want the hierarchy to remain in sync at all times, iterate through the bones property instead. /// public NativeArray rawLocalTransformsRW @@ -102,6 +104,24 @@ public NativeArray rawLocalTransformsRW return m_boneTransforms.Reinterpret().AsNativeArray().GetSubArray(m_currentBaseRootIndexWrite + boneCount, boneCount); } } + + /// + /// Specifies if the next sample will overwrite transforms (true) or if they will be added to existing transforms (false). + /// This value will automatically be set to false after each SamplePose() call made on this skeleton. + /// InertialBlend(), EndSamplingAndSync(), and CopyFromPrevious() will all set this value to true. + /// You may wish to explicitly set this value when using masked sampling to start and end animation override masks. + /// + public bool nextSampleWillOverwrite + { + get => (m_skeletonState.ValueRO.state & OptimizedSkeletonState.Flags.NextSampleShouldAdd) != OptimizedSkeletonState.Flags.NextSampleShouldAdd; + set + { + if (value) + m_skeletonState.ValueRW.state &= ~OptimizedSkeletonState.Flags.NextSampleShouldAdd; + else + m_skeletonState.ValueRW.state |= OptimizedSkeletonState.Flags.NextSampleShouldAdd; + } + } #endregion #region Modification Methods @@ -168,6 +188,7 @@ public void CopyFromPrevious() { var array = m_boneTransforms.AsNativeArray(); array.GetSubArray(m_currentBaseRootIndexWrite, boneCount * 2).CopyFrom(array.GetSubArray(m_previousBaseRootIndex, boneCount * 2)); + InitWeights(array.GetSubArray(m_currentBaseRootIndexWrite, boneCount * 2).Reinterpret()); ref var state = ref m_skeletonState.ValueRW.state; state |= OptimizedSkeletonState.Flags.IsDirty; state &= ~(OptimizedSkeletonState.Flags.NeedsSync | OptimizedSkeletonState.Flags.NextSampleShouldAdd); @@ -190,6 +211,7 @@ public void ForceInitialize() { m_boneTransforms.Resize(requiredBones * 6, NativeArrayOptions.UninitializedMemory); var array = m_boneTransforms.AsNativeArray(); + InitWeights(array.GetSubArray(0, requiredBones).Reinterpret()); array.GetSubArray(requiredBones, requiredBones).CopyFrom(array.GetSubArray(0, requiredBones)); Sync(true); array.GetSubArray(requiredBones * 2, requiredBones * 2).CopyFrom(array.GetSubArray(0, requiredBones * 2)); diff --git a/Kinemation/Components/OptimizedSkeletonAspectInternals.cs b/Kinemation/Components/OptimizedSkeletonAspectInternals.cs index 7bcc7cf..da105cb 100644 --- a/Kinemation/Components/OptimizedSkeletonAspectInternals.cs +++ b/Kinemation/Components/OptimizedSkeletonAspectInternals.cs @@ -70,8 +70,8 @@ unsafe void StartInertialBlendInternal(float previousDeltaTime, float maxBlendDu for (int i = 0; i < boneCount; i++) { - var currentNormalized = currentLocals[i]; - currentNormalized.rotation = math.normalize(currentNormalized.rotation); + var currentNormalized = currentLocals[i]; + currentNormalized.NormalizeBone(); inertialBlends[i].StartNewBlend(in currentNormalized, previousLocals[i], twoAgoLocals[i], rcpTime, maxBlendDuration); } } @@ -91,7 +91,7 @@ unsafe void InertialBlendInternal(float timeSinceStartOfBlend) var ptrs = (TransformQvvs*)currentLocals.GetUnsafePtr(); for (int i = 0; i < currentLocals.Length; i++) { - ptrs[i].rotation = math.normalize(ptrs[i].rotation); + ptrs[i].NormalizeBone(); inertialBlends[i].Blend(ref ptrs[i], in blendTimes); } } @@ -127,21 +127,28 @@ void Sync(bool forceSync0 = false) rootTransforms[0] = TransformQvvs.identity; for (int i = 1; i < boneCount; i++) { - var parent = math.max(0, parentIndices[i]); - var local = localTransforms[i]; - local.rotation.value = math.normalize(local.rotation.value); - rootTransforms[i] = qvvs.mul(rootTransforms[parent], in local); + var parent = math.max(0, parentIndices[i]); + var local = localTransforms[i]; + local.NormalizeBone(); + rootTransforms[i] = qvvs.mul(rootTransforms[parent], in local); } { - var local = localTransforms[0]; - local.rotation.value = math.normalize(local.rotation.value); - localTransforms[0] = local; - rootTransforms[0] = local; + var local = localTransforms[0]; + local.NormalizeBone(); + localTransforms[0] = local; + rootTransforms[0] = local; } m_skeletonState.ValueRW.state &= ~(OptimizedSkeletonState.Flags.NeedsSync | OptimizedSkeletonState.Flags.NextSampleShouldAdd); m_skeletonState.ValueRW.state |= OptimizedSkeletonState.Flags.IsDirty; SyncHistory(); } + + unsafe void InitWeights(NativeArray bones) + { + var ptr = (TransformQvvs*)bones.GetUnsafePtr(); + for (int i = 0; i < bones.Length; i++) + ptr[i].worldIndex = math.asint(1f); + } } public partial struct OptimizedBone diff --git a/Kinemation/Utilities/Blenders.cs b/Kinemation/Utilities/Blenders.cs index 744d6be..fb10b59 100644 --- a/Kinemation/Utilities/Blenders.cs +++ b/Kinemation/Utilities/Blenders.cs @@ -18,8 +18,8 @@ namespace Latios.Kinemation /// To discard existing sampled poses and begin sampling new poses, simply create a new /// instance of BufferPoseBlender using the same input NativeArray. /// - /// To finish sampling, call NormalizeRotations(). Prior to this, any transforms in the array - /// may have unnormalized rotations, which can cause unwanted artifacts if used as is. + /// To finish sampling, call Normalize(). Prior to this, any transforms in the array + /// may have unnormalized transforms, which can cause unwanted artifacts if used as is. /// /// You may also use a BufferPoseBlender to compute root-space transforms. /// @@ -37,20 +37,25 @@ public BufferPoseBlender(NativeArray localSpaceBuffer) { bufferAsQvvs = localSpaceBuffer.Reinterpret(); sampledFirst = false; - normalized = true; + normalized = false; } /// - /// Normalizes the rotations to be valid quaternion rotations. Call this once after sampling all blended poses. - /// The result of GetLocalTransformsView() will contain valid quaternion rotations after calling this method. - /// You can perform IK operations after calling this method but before calling ApplyBoneHierarchyAndFinish(). + /// Normalizes the transforms to be valid using the accumulated weights stored in translation.w of each bone. + /// Call this once after sampling all blended poses. + /// The buffer passed in will contain valid local-space transforms after calling this method. /// - public unsafe void NormalizeRotations() + public unsafe void Normalize() { - var bufferPtr = (TransformQvvs*)bufferAsQvvs.GetUnsafePtr(); + var bufferPtr = (AclUnity.Qvvs*)bufferAsQvvs.GetUnsafePtr(); for (int i = 0; i < bufferAsQvvs.Length; i++, bufferPtr++) { - bufferPtr->rotation = math.normalize(bufferPtr->rotation); + if (bufferPtr->translation.w == 1f) + continue; + var weight = 1f / bufferPtr->translation.w; + bufferPtr->rotation = math.normalize(bufferPtr->rotation); + bufferPtr->translation *= weight; + bufferPtr->stretchScale *= weight; } normalized = true; } @@ -60,41 +65,57 @@ public unsafe void NormalizeRotations() /// The transform at index 0 is assumed to represent root motion, and so all other transforms ignore it, /// even if they specify index 0 as a parent. /// - /// - /// - public void ComputeRootSpaceTransforms(ref BlobArray parentIndices, ref NativeArray rootSpaceBuffer) + /// The parent index of each bone + /// The buffer to write the destination values. This is allowed to be the same as the local-space buffer. + public void ComputeRootSpaceTransforms(ReadOnlySpan parentIndices, ref NativeArray rootSpaceBuffer) { var localSpaceBuffer = bufferAsQvvs.Reinterpret(); if (!normalized) { + var temp = localSpaceBuffer[0]; + temp.NormalizeBone(); rootSpaceBuffer[0] = TransformQvvs.identity; for (int i = 1; i < localSpaceBuffer.Length; i++) { - var parent = math.max(0, parentIndices[i]); - var local = localSpaceBuffer[i]; - local.rotation.value = math.normalize(local.rotation.value); - rootSpaceBuffer[i] = qvvs.mul(rootSpaceBuffer[parent], in local); + var parent = math.max(0, parentIndices[i]); + var local = localSpaceBuffer[i]; + local.NormalizeBone(); + rootSpaceBuffer[i] = qvvs.mul(rootSpaceBuffer[parent], in local); } { - var local = localSpaceBuffer[0]; - local.rotation.value = math.normalize(local.rotation.value); - localSpaceBuffer[0] = local; - rootSpaceBuffer[0] = local; + localSpaceBuffer[0] = temp; + rootSpaceBuffer[0] = temp; } } else { + var temp = localSpaceBuffer[0]; rootSpaceBuffer[0] = TransformQvvs.identity; for (int i = 1; i < localSpaceBuffer.Length; i++) { var parent = math.max(0, parentIndices[i]); rootSpaceBuffer[i] = qvvs.mul(rootSpaceBuffer[parent], localSpaceBuffer[i]); } - rootSpaceBuffer[0] = localSpaceBuffer[0]; + rootSpaceBuffer[0] = localSpaceBuffer[0] = temp; } } // Todo: Baked Root space? Custom parent indices? Convert to Root-Space in-place? Convert to World-Space? } + + public static class BoneNormalizationExtensions + { + public static void NormalizeBone(ref this TransformQvvs localTransform) + { + var w = math.asfloat(localTransform.worldIndex); + if (w == 1f) + return; + w = 1 / w; + ref var t = ref UnsafeUtility.As(ref localTransform); + t.rotation = math.normalize(t.rotation); + t.translation *= w; + t.stretchScale *= w; + } + } } diff --git a/Mimic/Mecanim/Systems/ApplyMecanimLayersToExposedBonesSystem.cs b/Mimic/Mecanim/Systems/ApplyMecanimLayersToExposedBonesSystem.cs index a5a8e06..75646ec 100644 --- a/Mimic/Mecanim/Systems/ApplyMecanimLayersToExposedBonesSystem.cs +++ b/Mimic/Mecanim/Systems/ApplyMecanimLayersToExposedBonesSystem.cs @@ -214,7 +214,7 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo clipSet.clips[clipWeights[i].mecanimClipIndex].SamplePose(ref blender, time, blendWeight); } - blender.NormalizeRotations(); + blender.Normalize(); // Begin write-back with inertial blending bool startInertialBlend = controller.triggerStartInertialBlend; diff --git a/PsyshockPhysics/Physics/Authoring/CompoundColliderSmartBlobberSystem.cs b/PsyshockPhysics/Physics/Authoring/CompoundColliderSmartBlobberSystem.cs index 3d09502..1104fcb 100644 --- a/PsyshockPhysics/Physics/Authoring/CompoundColliderSmartBlobberSystem.cs +++ b/PsyshockPhysics/Physics/Authoring/CompoundColliderSmartBlobberSystem.cs @@ -176,7 +176,7 @@ public void Execute(ref SmartBlobberResult result, in DynamicBuffer