542 lines
15 KiB
C#
542 lines
15 KiB
C#
using Cryville.Common;
|
|
using Cryville.Common.Buffers;
|
|
using Cryville.Common.Collections.Specialized;
|
|
using Cryville.Common.Pdt;
|
|
using Cryville.Crtr.Ruleset;
|
|
using Cryville.Crtr.Skin;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
using UnityEngine;
|
|
|
|
namespace Cryville.Crtr.Event {
|
|
public class ContainerState {
|
|
#region Struct
|
|
public EventBus Bus;
|
|
public EventContainer Container;
|
|
public StampedEvent StampedContainer;
|
|
public ContainerState Parent = null;
|
|
public ushort Depth;
|
|
|
|
public Dictionary<EventContainer, ContainerState> Children
|
|
= new();
|
|
HashSet<EventContainer> ActiveChildren
|
|
= new();
|
|
public Dictionary<Type, List<ContainerState>> TypedChildren
|
|
= new();
|
|
|
|
public ContainerState GetChild(int index, Type handlerType) {
|
|
var list = TypedChildren[handlerType];
|
|
if (index < 0 || index >= list.Count)
|
|
throw new ArgumentOutOfRangeException("index", string.Format("{0}#{1} does not exist.", list[0].Handler.TypeName, index));
|
|
return list[index];
|
|
}
|
|
|
|
private bool m_active;
|
|
public bool Active {
|
|
get { return m_active; }
|
|
private set {
|
|
if (m_active == value) return;
|
|
m_active = value;
|
|
if (!m_active) {
|
|
if (CloneType == 1) Dispose();
|
|
else if (CloneType >= 16) ReleasePools();
|
|
}
|
|
if (Parent != null) {
|
|
if (m_active) Parent.ActiveChildren.Add(Container);
|
|
else Parent.ActiveChildren.Remove(Container);
|
|
}
|
|
Bus.NotifyActiveChanged(this);
|
|
}
|
|
}
|
|
private bool m_lActive;
|
|
public bool LogicalActive {
|
|
get { return m_lActive; }
|
|
set {
|
|
if (m_lActive == value) return;
|
|
m_lActive = value;
|
|
UpdateActive();
|
|
if (m_lActive) Handler.StartLogicalUpdate(this);
|
|
else Handler.EndLogicalUpdate(this);
|
|
}
|
|
}
|
|
private bool m_pActive;
|
|
public bool PhysicalActive {
|
|
get { return m_pActive; }
|
|
set {
|
|
if (m_pActive == value) return;
|
|
m_pActive = value;
|
|
UpdateActive();
|
|
if (m_pActive) Handler.StartPhysicalUpdate(this);
|
|
else Handler.EndPhysicalUpdate(this);
|
|
}
|
|
}
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
void UpdateActive() { Active = m_lActive || m_pActive; }
|
|
|
|
public byte CloneType;
|
|
public ContainerState rootPrototype = null;
|
|
|
|
public ContainerHandler Handler {
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public double Time {
|
|
get {
|
|
return Bus.Time;
|
|
}
|
|
}
|
|
|
|
public ContainerState(EventContainer _ev, ContainerState parent = null) {
|
|
Container = _ev;
|
|
|
|
if (parent != null) {
|
|
AddChild(_ev, parent);
|
|
Parent = parent;
|
|
}
|
|
|
|
_rmvpa = new CategorizedPoolAccessor<int, RealtimeMotionValue>(RMVPool.Shared);
|
|
_mcpa = new CategorizedPoolAccessor<int, MotionCache>(MotionCachePool.Shared);
|
|
|
|
Values = new IntKeyedDictionary<RealtimeMotionValue>(ChartPlayer.motionRegistry.Count);
|
|
CachedValues = new IntKeyedDictionary<MotionCache>(ChartPlayer.motionRegistry.Count);
|
|
foreach (var m in ChartPlayer.motionRegistry)
|
|
Values.Add(m.Key.Key, new RealtimeMotionValue().Init(Parent == null ? m.Value.GlobalInitValue : m.Value.InitValue));
|
|
|
|
rootPrototype = this;
|
|
}
|
|
|
|
void AddChild(EventContainer c, ContainerState parent) {
|
|
parent.Children.Add(c, this);
|
|
Type t = c.GetType();
|
|
if (!parent.TypedChildren.TryGetValue(t, out List<ContainerState> tc))
|
|
parent.TypedChildren.Add(t, tc = new List<ContainerState>());
|
|
tc.Add(this);
|
|
}
|
|
|
|
public ContainerState Clone(byte ct) {
|
|
var r = (ContainerState)MemberwiseClone();
|
|
|
|
var mvs = new IntKeyedDictionary<RealtimeMotionValue>(ChartPlayer.motionRegistry.Count);
|
|
foreach (var mv in Values) {
|
|
mvs.Add(mv.Key, mv.Value.Clone());
|
|
}
|
|
r.Values = mvs;
|
|
|
|
var cvs = new IntKeyedDictionary<MotionCache>(ChartPlayer.motionRegistry.Count);
|
|
r.CachedValues = cvs;
|
|
|
|
r.Children = new Dictionary<EventContainer, ContainerState>();
|
|
r.TypedChildren = new Dictionary<Type, List<ContainerState>>();
|
|
foreach (var child in Children) {
|
|
var cc = child.Value.Clone(ct);
|
|
cc.Parent = r;
|
|
cc.AddChild(child.Key, r);
|
|
}
|
|
|
|
r.ActiveChildren = new HashSet<EventContainer>();
|
|
|
|
var pms = new Dictionary<StampedEvent, RealtimeMotionValue>(Math.Max(4, PlayingMotions.Count));
|
|
foreach (var m in PlayingMotions)
|
|
pms.Add(m.Key, m.Value);
|
|
r.PlayingMotions = pms;
|
|
|
|
if (ct == 1) Handler.bs = r;
|
|
else if (ct == 2) Handler.ts = r;
|
|
else if (ct == 3) Handler.ns = r;
|
|
else if (ct >= 16) Handler.ps = r;
|
|
else throw new InvalidOperationException("Invalid clone type");
|
|
r.CloneType = ct;
|
|
return r;
|
|
}
|
|
|
|
public void CopyTo(ContainerState dest) {
|
|
dest.m_lActive = m_lActive;
|
|
dest.m_pActive = m_pActive;
|
|
dest.m_active = m_active;
|
|
if (dest.m_active) dest.Bus.NotifyActiveChanged(dest);
|
|
|
|
foreach (var mv in Values) {
|
|
if (dest.Values.TryGetValue(mv.Key, out RealtimeMotionValue dv)) mv.Value.CopyTo(dv, false);
|
|
else dest.Values.Add(mv.Key, mv.Value.Clone());
|
|
}
|
|
|
|
foreach (var cv in dest.CachedValues) cv.Value.Valid = false;
|
|
foreach (var cv in CachedValues) {
|
|
if (!dest.CachedValues.TryGetValue(cv.Key, out MotionCache dv)) {
|
|
dest.CachedValues.Add(cv.Key, dv = dest._mcpa.Rent(cv.Key));
|
|
}
|
|
cv.Value.CopyTo(dv);
|
|
}
|
|
|
|
foreach (var cev in dest.ActiveChildren) {
|
|
if (!ActiveChildren.Contains(cev))
|
|
Children[cev].CopyTo(dest.Children[cev]);
|
|
}
|
|
dest.ActiveChildren.Clear();
|
|
foreach (var cev in ActiveChildren) {
|
|
dest.ActiveChildren.Add(cev);
|
|
Children[cev].CopyTo(dest.Children[cev]);
|
|
}
|
|
|
|
foreach (var m in dest.PlayingMotions) {
|
|
if (m.Value.CloneTypeFlag == dest.CloneType) dest._rmvpa.Return(m.Value);
|
|
}
|
|
dest.PlayingMotions.Clear();
|
|
foreach (var m in PlayingMotions) {
|
|
dest.PlayingMotions.Add(m.Key, m.Value);
|
|
}
|
|
}
|
|
|
|
public bool Disposed {
|
|
get;
|
|
private set;
|
|
}
|
|
public void Dispose() {
|
|
if (Disposed) return;
|
|
Disposed = true;
|
|
if (CloneType == 1) Handler.Dispose();
|
|
ReleasePools();
|
|
foreach (var s in Children)
|
|
s.Value.Dispose();
|
|
}
|
|
public void DisposeAll() {
|
|
foreach (var s in Children)
|
|
s.Value.DisposeAll();
|
|
Handler.DisposeAll();
|
|
}
|
|
public void ReleasePools() {
|
|
_rmvpa.ReturnAll();
|
|
_mcpa.ReturnAll();
|
|
}
|
|
|
|
public void AttachHandler(ContainerHandler h) {
|
|
if (Handler != null)
|
|
throw new InvalidOperationException("Handler attached twice");
|
|
Handler = h;
|
|
h.cs = this;
|
|
}
|
|
|
|
public void AttachSystems(PdtSkin skin, Judge judge) {
|
|
Handler.AttachSystems(skin, judge);
|
|
}
|
|
#endregion
|
|
|
|
#region Motion
|
|
readonly CategorizedPoolAccessor<int, RealtimeMotionValue> _rmvpa;
|
|
readonly CategorizedPoolAccessor<int, MotionCache> _mcpa;
|
|
Dictionary<StampedEvent, RealtimeMotionValue> PlayingMotions = new(4);
|
|
IntKeyedDictionary<RealtimeMotionValue> Values;
|
|
IntKeyedDictionary<MotionCache> CachedValues;
|
|
|
|
void InvalidateMotion(int name) {
|
|
if (!CachedValues.TryGetValue(name, out MotionCache cache))
|
|
CachedValues.Add(name, cache = _mcpa.Rent(name));
|
|
cache.Valid = false;
|
|
foreach (var c in ActiveChildren)
|
|
Children[c].InvalidateMotion(name);
|
|
}
|
|
|
|
public Vector GetComputedValue(int key) {
|
|
if (!CachedValues.TryGetValue(key, out MotionCache tr))
|
|
CachedValues.Add(key, tr = _mcpa.Rent(key));
|
|
Vector r = tr.Value;
|
|
if (tr.Valid) return r;
|
|
Values[key].Compute(ref r);
|
|
if (Parent != null) r.ApplyFrom(Parent.GetComputedValue(key));
|
|
tr.Valid = true;
|
|
return r;
|
|
}
|
|
|
|
public T GetComputedValue<T>(int key) where T : Vector {
|
|
return (T)GetComputedValue(key);
|
|
}
|
|
|
|
static readonly int n_pt = IdentifierManager.Shared.Request("pt");
|
|
public Vector2 ScreenPoint {
|
|
get {
|
|
var mv = GetComputedValue<Vec2>(n_pt);
|
|
return mv.ToVector2();
|
|
}
|
|
}
|
|
|
|
static readonly int n_dir = IdentifierManager.Shared.Request("dir");
|
|
public Vector3 Direction {
|
|
get {
|
|
Vec3 r = GetComputedValue<Vec3>(n_dir);
|
|
return r.ToVector3();
|
|
}
|
|
}
|
|
|
|
static readonly int n_normal = IdentifierManager.Shared.Request("normal");
|
|
public Vector3 Normal {
|
|
get {
|
|
Vec3 r = GetComputedValue<Vec3>(n_normal);
|
|
return r.ToVector3();
|
|
}
|
|
}
|
|
|
|
public Quaternion QuatDir {
|
|
get {
|
|
return Quaternion.LookRotation(Quaternion.Euler(Direction) * Vector3.forward, Normal);
|
|
}
|
|
}
|
|
|
|
static readonly int n_sv = IdentifierManager.Shared.Request("sv");
|
|
static readonly int n_svm = IdentifierManager.Shared.Request("svm");
|
|
public float ScrollVelocity {
|
|
get {
|
|
return GetComputedValue<Vec1>(n_sv).Value * GetComputedValue<Vec1m>(n_svm).Value;
|
|
}
|
|
}
|
|
|
|
static readonly int n_dist = IdentifierManager.Shared.Request("dist");
|
|
public float Distance {
|
|
get {
|
|
return GetComputedValue<Vec1>(n_dist).Value;
|
|
}
|
|
}
|
|
|
|
static readonly int n_track = IdentifierManager.Shared.Request("track");
|
|
public float Track {
|
|
get {
|
|
return GetComputedValue<Vec1>(n_track).Value;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Update
|
|
bool breakflag = false;
|
|
|
|
public void Break() {
|
|
// Handler.EndLogicalUpdate(this);
|
|
breakflag = true;
|
|
// LogicalActive = false;
|
|
}
|
|
|
|
public void Discard(StampedEvent ev) {
|
|
Handler.Discard(this, ev);
|
|
if (ev is StampedEvent.RelativeMotion motion) {
|
|
ReturnRelativeMotionEvent(motion);
|
|
}
|
|
else if (ev.Origin is StampedEvent.RelativeMotion) {
|
|
ReturnEndRelativeMotionEvent((StampedEvent.Temporary)ev);
|
|
}
|
|
}
|
|
|
|
public void Handle(StampedEvent ev) {
|
|
if (breakflag) return;
|
|
if (ev != null) {
|
|
if (ev.Unstamped is Chart.Motion) {
|
|
var tev = (Chart.Motion)ev.Unstamped;
|
|
var mv = _rmvpa.Rent(tev.Name.Key);
|
|
mv.CloneTypeFlag = CloneType;
|
|
Values[tev.Name.Key].CopyTo(mv, true);
|
|
PlayingMotions.Add(ev, mv);
|
|
Update(ev);
|
|
if (!ev.Unstamped.IsLong) {
|
|
PlayingMotions.Remove(ev);
|
|
_rmvpa.Return(mv);
|
|
}
|
|
}
|
|
else if (ev is StampedEvent.RelativeMotion tev) {
|
|
var mv = _rmvpa.Rent(tev.Name);
|
|
mv.CloneTypeFlag = CloneType;
|
|
Values[tev.Name].CopyTo(mv, true);
|
|
PlayingMotions.Add(ev, mv);
|
|
Update(ev);
|
|
if (ev.Duration == 0) {
|
|
PlayingMotions.Remove(ev);
|
|
_rmvpa.Return(mv);
|
|
ReturnRelativeMotionEvent(tev);
|
|
}
|
|
}
|
|
else if (ev.Unstamped is EventContainer) {
|
|
var cev = (EventContainer)ev.Unstamped;
|
|
var ccs = Children[cev];
|
|
ccs.LogicalActive = true;
|
|
UpdateMotions();
|
|
if (!cev.IsLong) {
|
|
ccs.LogicalActive = false;
|
|
}
|
|
}
|
|
else if (ev.Origin != null) {
|
|
var oev = ev.Origin;
|
|
if (oev is StampedEvent.RelativeMotion motion) {
|
|
Update(ev);
|
|
var mv = PlayingMotions[oev];
|
|
if (mv.CloneTypeFlag == CloneType) _rmvpa.Return(mv);
|
|
PlayingMotions.Remove(oev);
|
|
ReturnEndRelativeMotionEvent((StampedEvent.Temporary)ev);
|
|
ReturnRelativeMotionEvent(motion);
|
|
}
|
|
else {
|
|
var nev = oev.Unstamped;
|
|
if (nev is Chart.Motion) {
|
|
Update(ev);
|
|
var mv = PlayingMotions[oev];
|
|
if (mv.CloneTypeFlag == CloneType) _rmvpa.Return(mv);
|
|
PlayingMotions.Remove(oev);
|
|
}
|
|
else if (nev is EventContainer cev) {
|
|
var ccs = Children[cev];
|
|
UpdateMotions();
|
|
ccs.LogicalActive = false;
|
|
}
|
|
}
|
|
}
|
|
Update(ev.Unstamped == null || ev.Unstamped.Priority >= 0 ? ev : null);
|
|
}
|
|
else Update(null);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
void Update(StampedEvent ev) {
|
|
UpdateMotions();
|
|
Handler.Update(this, ev);
|
|
}
|
|
|
|
private void UpdateMotions() {
|
|
foreach (var m in PlayingMotions) {
|
|
if (m.Key is StampedEvent.RelativeMotion) {
|
|
var tev = (StampedEvent.RelativeMotion)m.Key;
|
|
var value = Values[tev.Name];
|
|
InvalidateMotion(tev.Name);
|
|
if (tev.Duration == 0) {
|
|
tev.Node.Value.CopyTo(value.RelativeValue);
|
|
}
|
|
else {
|
|
var scaledTime = (float)((Time - tev.Time) / tev.Duration);
|
|
var transition = GetTransition(scaledTime, tev.Node.Transition);
|
|
tev.Node.Value.LerpWith(m.Value.RelativeValue, transition, ref value.RelativeValue);
|
|
}
|
|
}
|
|
else {
|
|
var tev = (Chart.Motion)m.Key.Unstamped;
|
|
if (tev.Node.Id >= 0 && (CloneType == 2 || CloneType == 3)) continue;
|
|
var value = Values[tev.Name.Key];
|
|
InvalidateMotion(tev.Name.Key);
|
|
if (m.Key.Duration == 0) {
|
|
if (tev.Node.Id >= 0) {
|
|
if (CloneType != 0) continue;
|
|
value.SetRelativeNode(tev.Node);
|
|
}
|
|
else {
|
|
tev.Node.Value.CopyTo(value.AbsoluteValue);
|
|
}
|
|
}
|
|
else {
|
|
var scaledTime = (float)((Time - m.Key.Time - ChartPlayer.actualRenderStep * tev.sumfix) / m.Key.Duration);
|
|
var transition = GetTransition(scaledTime, tev.transition);
|
|
if (tev.Node.Id >= 0) {
|
|
if (CloneType != 0) continue;
|
|
var start = m.Value.GetRelativeNode(tev.Node.Id);
|
|
if (start == null) {
|
|
value.SetRelativeNode(tev.Node);
|
|
}
|
|
else {
|
|
var target = value.GetRelativeNode(tev.Node.Id);
|
|
if (target == null) value.SetRelativeNode(tev.Node);
|
|
else tev.Node.LerpWith(start, transition, ref target);
|
|
}
|
|
}
|
|
else {
|
|
tev.Node.Value.LerpWith(m.Value.AbsoluteValue, transition, ref value.AbsoluteValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static readonly PropStores.Float _ttimest = new();
|
|
static readonly PropStores.Vector4 _transst = new();
|
|
Vector4 GetTransition(float time, PdtExpression transition) {
|
|
if (time >= 1) return Vector4.one;
|
|
if (transition == null) return new Vector4(time, time, time, time);
|
|
_ttimest.Value = time;
|
|
PdtEvaluator.Instance.ContextSelfValue = _ttimest.Source;
|
|
PdtEvaluator.Instance.Evaluate(_transst.Target, transition);
|
|
PdtEvaluator.Instance.ContextSelfValue = null;
|
|
return _transst.Value;
|
|
}
|
|
|
|
public void BroadcastPreInit() {
|
|
Handler.PreInit();
|
|
foreach (var c in Children) {
|
|
c.Value.BroadcastPreInit();
|
|
}
|
|
}
|
|
public void BroadcastPostInit() {
|
|
Handler.PostInit();
|
|
foreach (var c in Children) {
|
|
c.Value.BroadcastPostInit();
|
|
}
|
|
}
|
|
|
|
public void EndPreGraphicalUpdate() {
|
|
Handler.SetPreGraphicalActive(false, this);
|
|
foreach (var ls in ActiveChildren) {
|
|
Children[ls].EndPreGraphicalUpdate();
|
|
}
|
|
}
|
|
|
|
public void EndGraphicalUpdate() {
|
|
Handler.SetGraphicalActive(false, this);
|
|
foreach (var ls in ActiveChildren) {
|
|
Children[ls].EndGraphicalUpdate();
|
|
}
|
|
}
|
|
|
|
static readonly SimpleObjectPool<StampedEvent.RelativeMotion> relmEvPool
|
|
= new(1024);
|
|
static readonly SimpleObjectPool<StampedEvent.Temporary> erelmEvPool
|
|
= new(1024);
|
|
public void PreAnchor() {
|
|
PushRelativeMotions(Handler.ns.Bus);
|
|
Handler.PreAnchor();
|
|
foreach (var ls in Children) {
|
|
if (ls.Value.Handler.Alive) ls.Value.PreAnchor();
|
|
}
|
|
}
|
|
public void Anchor() {
|
|
PushRelativeMotions(Handler.ts.Bus);
|
|
Handler.Anchor();
|
|
foreach (var ls in Children) {
|
|
if (ls.Value.Handler.Alive) ls.Value.Anchor();
|
|
}
|
|
}
|
|
void PushRelativeMotions(EventBus bus) {
|
|
foreach (var m in Values) {
|
|
foreach (var node in m.Value.RelativeNodes) {
|
|
var ev = relmEvPool.Rent();
|
|
ev.Time = rootPrototype.Time + node.Value.Time.Value;
|
|
ev.Container = Container;
|
|
ev.Name = m.Key;
|
|
ev.Node = node.Value;
|
|
bus.PushTempEvent(ev);
|
|
if (node.Value.EndTime.Value > node.Value.Time.Value) {
|
|
var eev = erelmEvPool.Rent();
|
|
eev.Time = rootPrototype.Time + node.Value.EndTime.Value;
|
|
eev.Container = Container;
|
|
eev.Origin = ev;
|
|
ev.ReleaseEvent = eev;
|
|
bus.PushTempEvent(eev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void ReturnRelativeMotionEvent(StampedEvent.RelativeMotion ev) {
|
|
ev.Container = null;
|
|
ev.Node = null;
|
|
ev.ReleaseEvent = null;
|
|
relmEvPool.Return(ev);
|
|
}
|
|
void ReturnEndRelativeMotionEvent(StampedEvent.Temporary ev) {
|
|
ev.Container = null;
|
|
ev.Origin = null;
|
|
erelmEvPool.Return(ev);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|