Files
crtr/Assets/Cryville/Crtr/Skin/SkinContainer.cs

216 lines
7.7 KiB
C#

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<SkinPropertyKey> _once = new();
public readonly IntKeyedDictionary<PropStores.Float> Variables = new();
class DynamicStack {
public readonly List<DynamicProperty> Properties = new();
public readonly List<DynamicElement> 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<PropSrc> PropSrcs { get; private set; }
public SkinContext(Transform transform, IntKeyedDictionary<PropSrc> 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<Anchor> result);
void RegisterAnchor(int name);
void PushAnchorEvent(double time, int name);
}
}