216 lines
7.7 KiB
C#
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);
|
|
}
|
|
}
|