2024-08-04 10:56:07 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using nadena.dev.ndmf.preview;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
namespace nadena.dev.modular_avatar.core.editor
|
|
|
|
|
{
|
|
|
|
|
internal class ObjectSwitcherPreview : IRenderFilter
|
|
|
|
|
{
|
2024-08-04 12:05:10 +08:00
|
|
|
|
static TogglablePreviewNode EnableNode = TogglablePreviewNode.Create(
|
|
|
|
|
() => "Object Switcher",
|
|
|
|
|
qualifiedName: "nadena.dev.modular-avatar/ObjectSwitcherPreview",
|
|
|
|
|
true
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
public IEnumerable<TogglablePreviewNode> GetPreviewControlNodes()
|
|
|
|
|
{
|
|
|
|
|
yield return EnableNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool IsEnabled(ComputeContext context)
|
|
|
|
|
{
|
|
|
|
|
return context.Observe(EnableNode.IsEnabled);
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-04 10:56:07 +08:00
|
|
|
|
public ImmutableList<RenderGroup> GetTargetGroups(ComputeContext context)
|
|
|
|
|
{
|
2024-08-05 10:31:43 +08:00
|
|
|
|
var menuItemPreview = new MenuItemPreviewCondition(context);
|
2024-08-04 10:56:07 +08:00
|
|
|
|
var allToggles = context.GetComponentsByType<ModularAvatarObjectToggle>();
|
|
|
|
|
|
|
|
|
|
var objectGroups =
|
|
|
|
|
new Dictionary<GameObject, ImmutableList<(ModularAvatarObjectToggle, int)>.Builder>(
|
|
|
|
|
new ObjectIdentityComparer<GameObject>());
|
|
|
|
|
|
|
|
|
|
foreach (var toggle in allToggles)
|
|
|
|
|
{
|
2024-08-05 10:31:43 +08:00
|
|
|
|
var mami = context.GetComponent<ModularAvatarMenuItem>(toggle.gameObject);
|
2024-08-11 09:03:50 +08:00
|
|
|
|
|
|
|
|
|
bool active = context.ActiveAndEnabled(toggle) && (mami == null || menuItemPreview.IsEnabledForPreview(mami));
|
|
|
|
|
if (active == context.Observe(toggle, t => t.Inverted)) continue;
|
2024-08-05 10:31:43 +08:00
|
|
|
|
|
2024-08-04 10:56:07 +08:00
|
|
|
|
context.Observe(toggle,
|
|
|
|
|
t => t.Objects.Select(o => o.Object.referencePath).ToList(),
|
|
|
|
|
(x, y) => x.SequenceEqual(y)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (toggle.Objects == null) continue;
|
|
|
|
|
|
|
|
|
|
var index = -1;
|
|
|
|
|
foreach (var switched in toggle.Objects)
|
|
|
|
|
{
|
|
|
|
|
index++;
|
|
|
|
|
|
|
|
|
|
if (switched.Object == null) continue;
|
|
|
|
|
|
|
|
|
|
var target = context.Observe(toggle, _ => switched.Object.Get(toggle));
|
|
|
|
|
|
|
|
|
|
if (target == null) continue;
|
|
|
|
|
|
|
|
|
|
if (!objectGroups.TryGetValue(target, out var group))
|
|
|
|
|
{
|
|
|
|
|
group = ImmutableList.CreateBuilder<(ModularAvatarObjectToggle, int)>();
|
|
|
|
|
objectGroups[target] = group;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
group.Add((toggle, index));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var affectedRenderers = objectGroups.Keys
|
|
|
|
|
.SelectMany(go => context.GetComponentsInChildren<Renderer>(go, true))
|
|
|
|
|
// If we have overlapping objects, we need to sort by child to parent, so parent configuration overrides
|
|
|
|
|
// the child. We do this by simply looking at how many times we observe each renderer.
|
|
|
|
|
.GroupBy(r => r)
|
|
|
|
|
.Select(g => g.Key)
|
2024-08-05 10:31:43 +08:00
|
|
|
|
.ToHashSet();
|
2024-08-04 10:56:07 +08:00
|
|
|
|
|
|
|
|
|
var renderGroups = new List<RenderGroup>();
|
2024-08-05 10:31:43 +08:00
|
|
|
|
|
2024-08-04 10:56:07 +08:00
|
|
|
|
foreach (var r in affectedRenderers)
|
|
|
|
|
{
|
2024-08-05 10:31:43 +08:00
|
|
|
|
var shouldEnable = true;
|
|
|
|
|
|
2024-08-04 10:56:07 +08:00
|
|
|
|
var obj = r.gameObject;
|
2024-08-05 10:31:43 +08:00
|
|
|
|
context.ActiveInHierarchy(obj); // observe path changes & object state changes
|
|
|
|
|
|
2024-08-04 10:56:07 +08:00
|
|
|
|
while (obj != null)
|
|
|
|
|
{
|
2024-08-05 10:31:43 +08:00
|
|
|
|
var enableAtNode = obj.activeSelf;
|
|
|
|
|
|
2024-08-04 10:56:07 +08:00
|
|
|
|
var group = objectGroups.GetValueOrDefault(obj);
|
2024-08-05 10:31:43 +08:00
|
|
|
|
if (group == null && !obj.activeSelf)
|
|
|
|
|
{
|
|
|
|
|
// always inactive
|
|
|
|
|
shouldEnable = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (group != null)
|
|
|
|
|
{
|
|
|
|
|
var (toggle, index) = group[^1];
|
2024-08-11 09:03:50 +08:00
|
|
|
|
enableAtNode = context.Observe(toggle, t => t.Objects.Count > index && t.Objects[index].Active);
|
2024-08-05 10:31:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!enableAtNode)
|
|
|
|
|
{
|
|
|
|
|
shouldEnable = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-08-04 10:56:07 +08:00
|
|
|
|
|
|
|
|
|
obj = obj.transform.parent?.gameObject;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-08 09:19:49 +08:00
|
|
|
|
if (shouldEnable != r.gameObject.activeInHierarchy)
|
|
|
|
|
renderGroups.Add(RenderGroup.For(r).WithData(shouldEnable));
|
2024-08-04 10:56:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return renderGroups.ToImmutableList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task<IRenderFilterNode> Instantiate(RenderGroup group, IEnumerable<(Renderer, Renderer)> proxyPairs,
|
|
|
|
|
ComputeContext context)
|
|
|
|
|
{
|
2024-08-08 09:19:49 +08:00
|
|
|
|
return Task.FromResult<IRenderFilterNode>(new Node(group.GetData<bool>()));
|
2024-08-04 10:56:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Node : IRenderFilterNode
|
|
|
|
|
{
|
|
|
|
|
public RenderAspects WhatChanged => 0;
|
2024-08-08 09:19:49 +08:00
|
|
|
|
private readonly bool _shouldEnable;
|
2024-08-04 10:56:07 +08:00
|
|
|
|
|
2024-08-08 09:19:49 +08:00
|
|
|
|
public Node(bool shouldEnable)
|
|
|
|
|
{
|
|
|
|
|
_shouldEnable = shouldEnable;
|
|
|
|
|
}
|
2024-08-05 10:31:43 +08:00
|
|
|
|
|
2024-08-04 10:56:07 +08:00
|
|
|
|
public void OnFrame(Renderer original, Renderer proxy)
|
|
|
|
|
{
|
2024-08-08 09:19:49 +08:00
|
|
|
|
proxy.gameObject.SetActive(_shouldEnable);
|
2024-08-04 10:56:07 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|