using Cryville.Common.Collections.Specialized; using Cryville.Common.Pdt; using System.Collections.Generic; using UnityEngine; using UnityEngine.Profiling; namespace Cryville.Crtr.Skin { public class SkinContainer { readonly ISkinnableGroup _group; readonly SkinElement _rootElement; readonly DynamicStack[] _stacks = new DynamicStack[3]; readonly HashSet _once = new(); public readonly IntKeyedDictionary Variables = new(); class DynamicStack { public readonly List Properties = new(); public readonly List Elements = new(); public void Clear() { Properties.Clear(); Elements.Clear(); } } struct DynamicProperty { public RuntimeSkinContext Context { get; set; } public SkinPropertyKey Key { get; set; } public PdtExpression Value { get; set; } } struct DynamicElement { public RuntimeSkinContext Context { get; set; } public SkinSelectors Selectors { get; set; } public SkinElement Element { get; set; } } public SkinContainer(ISkinnableGroup group, SkinElement rootElement) { _group = group; _rootElement = rootElement; for (int i = 0; i < _stacks.Length; i++) _stacks[i] = new DynamicStack(); } public void MatchStatic() { var stack = _stacks[0]; stack.Clear(); MatchStatic(_rootElement, stack, new RuntimeSkinContext(_group.SkinContext)); } void MatchStatic(SkinElement rel, DynamicStack stack, RuntimeSkinContext ctx) { var rc = ctx.ReadContext; PdtEvaluator.Instance.ContextTransform = rc.Transform; if (rc.PropSrcs != null) PdtEvaluator.Instance.ContextCascadeInsert(rc.PropSrcs); foreach (var p in rel.properties) { try { p.Key.ExecuteStatic(_group, ctx, p.Value, Variables); } catch (EvaluationFailureException) { } if (p.Key.IsValueRequired && !p.Value.IsConstant) stack.Properties.Add( new DynamicProperty { Context = ctx, Key = p.Key, Value = p.Value } ); } PdtEvaluator.Instance.ContextTransform = null; foreach (var e in rel.elements) { try { var nctxs = e.Key.MatchStatic(_group, rc); if (nctxs != null) { var roflag = e.Key.annotations.Contains("if"); var woflag = e.Key.annotations.Contains("then"); foreach (var nctx in nctxs) { var nrctx = new RuntimeSkinContext(nctx, ctx, roflag, woflag); MatchStatic(e.Value, stack, nrctx); } } } catch (SelectorNotAvailableException) { stack.Elements.Add( new DynamicElement { Context = ctx, Selectors = e.Key, Element = e.Value } ); } } if (rc.PropSrcs != null) PdtEvaluator.Instance.ContextCascadeDiscard(); } public void MatchDynamic(int dl, bool recursive = false) { var stack = _stacks[dl]; if (stack.Properties.Count == 0 && stack.Elements.Count == 0) return; var nstack = dl + 1 < _stacks.Length ? _stacks[dl + 1] : null; nstack?.Clear(); Profiler.BeginSample("SkinContainer.MatchDynamic"); if (!recursive) PdtEvaluator.Instance.ContextSkinContainer = this; for (int i = 0; i < stack.Properties.Count; i++) { DynamicProperty p = stack.Properties[i]; p.Key.ExecuteDynamic(_group, p.Context, p.Value, Variables, dl); if (p.Key.annotations.Contains("once")) { stack.Properties.RemoveAt(i--); } if (p.Key.IsValueRequired && !p.Value.IsConstant && dl < 1) nstack.Properties.Add(p); } for (int i = 0; i < stack.Elements.Count; i++) { DynamicElement e = stack.Elements[i]; var psrcs = e.Context.ReadContext.PropSrcs; if (psrcs != null) PdtEvaluator.Instance.ContextCascadeInsert(psrcs); if (e.Selectors.IsUpdatable(_group, dl)) { try { var nctx = e.Selectors.MatchDynamic(_group, e.Context.ReadContext); if (nctx != null) { MatchDynamic(e.Element, dl, nstack, new RuntimeSkinContext( nctx, e.Context, e.Selectors.annotations.Contains("if"), e.Selectors.annotations.Contains("then") )); if (e.Selectors.annotations.Contains("once")) { stack.Elements.RemoveAt(i--); } } } catch (SelectorNotAvailableException) { if (nstack == null) throw; nstack.Elements.Add(e); } } else { if (nstack == null) throw new SelectorNotAvailableException(); nstack.Elements.Add(e); } if (psrcs != null) PdtEvaluator.Instance.ContextCascadeDiscard(); } if (!recursive) PdtEvaluator.Instance.ContextSkinContainer = null; Profiler.EndSample(); } void MatchDynamic(SkinElement rel, int dl, DynamicStack stack, RuntimeSkinContext ctx) { var rc = ctx.ReadContext; PdtEvaluator.Instance.ContextTransform = rc.Transform; if (rc.PropSrcs != null) PdtEvaluator.Instance.ContextCascadeInsert(rc.PropSrcs); foreach (var p in rel.properties) { if (p.Key.annotations.Contains("once")) { if (_once.Contains(p.Key)) continue; p.Key.ExecuteDynamic(_group, ctx, p.Value, Variables, dl); _once.Add(p.Key); } else { p.Key.ExecuteDynamic(_group, ctx, p.Value, Variables, dl); } } PdtEvaluator.Instance.ContextTransform = null; foreach (var e in rel.elements) { if (e.Key.IsUpdatable(_group, dl)) { SkinContext nctx = e.Key.MatchDynamic(_group, rc); if (nctx != null) MatchDynamic(e.Value, dl, stack, new RuntimeSkinContext( nctx, ctx, e.Key.annotations.Contains("if"), e.Key.annotations.Contains("then") )); } else { if (stack == null) throw new SelectorNotAvailableException(); stack.Elements.Add( new DynamicElement { Context = ctx, Selectors = e.Key, Element = e.Value } ); } } if (rc.PropSrcs != null) PdtEvaluator.Instance.ContextCascadeDiscard(); } readonly PropStores.Float _rtimest = new(); public void MatchAnimation(AnimationSpan span, float rtime, RuntimeSkinContext ctx) { PdtEvaluator.Instance.ContextSkinContainer = this; PdtEvaluator.Instance.ContextSelfValue = _rtimest.Source; MatchAnimationInternal(span, rtime, ctx); PdtEvaluator.Instance.ContextSelfValue = null; PdtEvaluator.Instance.ContextSkinContainer = null; } void MatchAnimationInternal(AnimationSpan span, float rtime, RuntimeSkinContext ctx) { _rtimest.Value = rtime; foreach (var p in span.properties) { p.Key.ExecuteDynamic(_group, ctx, p.Value, Variables, 0); } foreach (var s in span.spans) { if (rtime < s.Key.Behind || rtime >= s.Key.Ahead) continue; MatchAnimationInternal(s.Value, (rtime - s.Key.Behind) / (s.Key.Ahead - s.Key.Behind), ctx); } } } public class SkinContext { public Transform Transform { get; private set; } public IntKeyedDictionary PropSrcs { get; private set; } public SkinContext(Transform transform, IntKeyedDictionary propSrcs = null) { Transform = transform; PropSrcs = propSrcs; } } public struct RuntimeSkinContext { public SkinContext ReadContext { get; private set; } public Transform WriteTransform { get; private set; } public RuntimeSkinContext(SkinContext rw) { ReadContext = rw; WriteTransform = rw.Transform; } public RuntimeSkinContext(SkinContext r, Transform w) { ReadContext = r; WriteTransform = w; } public RuntimeSkinContext(SkinContext newctx, RuntimeSkinContext oldctx, bool roflag, bool woflag) { if (roflag) { ReadContext = newctx; WriteTransform = oldctx.WriteTransform; } else if (woflag) { ReadContext = oldctx.ReadContext; WriteTransform = newctx.Transform; } else { ReadContext = newctx; WriteTransform = newctx.Transform; } } } public interface ISkinnableGroup { string TypeName { get; } SkinContext SkinContext { get; } int AtAnchorDynamicLevel { get; } int OpenedAnchorName { get; } bool TryGetAnchorsByName(int name, out IReadOnlyCollection result); void RegisterAnchor(int name); void PushAnchorEvent(double time, int name); } }