2024-06-03 08:52:08 +08:00
|
|
|
|
#region
|
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
using System;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using nadena.dev.ndmf.preview;
|
|
|
|
|
using UnityEngine;
|
2024-06-07 11:58:08 +08:00
|
|
|
|
using Object = UnityEngine.Object;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
namespace nadena.dev.modular_avatar.core.editor
|
|
|
|
|
{
|
|
|
|
|
public class ShapeChangerPreview : IRenderFilter
|
|
|
|
|
{
|
2024-07-02 11:38:27 +08:00
|
|
|
|
public ImmutableList<RenderGroup> GetTargetGroups(ComputeContext ctx)
|
|
|
|
|
{
|
|
|
|
|
var allChangers = ctx.GetComponentsByType<ModularAvatarShapeChanger>();
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-07-02 11:38:27 +08:00
|
|
|
|
var groups =
|
|
|
|
|
new Dictionary<Renderer, ImmutableList<ModularAvatarShapeChanger>.Builder>(
|
|
|
|
|
new ObjectIdentityComparer<Renderer>());
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-07-02 11:38:27 +08:00
|
|
|
|
foreach (var changer in allChangers)
|
|
|
|
|
{
|
|
|
|
|
if (changer == null) continue;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-07-02 11:38:27 +08:00
|
|
|
|
// TODO: observe avatar root
|
|
|
|
|
if (!ctx.ActiveAndEnabled(changer)) continue;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-07-29 09:53:23 +08:00
|
|
|
|
var target = ctx.Observe(changer, _ => changer.targetRenderer.Get(changer));
|
2024-07-02 11:38:27 +08:00
|
|
|
|
var renderer = ctx.GetComponent<SkinnedMeshRenderer>(target);
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-07-02 11:38:27 +08:00
|
|
|
|
if (renderer == null) continue;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-07-02 11:38:27 +08:00
|
|
|
|
if (!groups.TryGetValue(renderer, out var group))
|
|
|
|
|
{
|
|
|
|
|
group = ImmutableList.CreateBuilder<ModularAvatarShapeChanger>();
|
|
|
|
|
groups[renderer] = group;
|
|
|
|
|
}
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-07-02 11:38:27 +08:00
|
|
|
|
group.Add(changer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return groups.Select(g => RenderGroup.For(g.Key).WithData(g.Value.ToImmutable()))
|
|
|
|
|
.ToImmutableList();
|
|
|
|
|
}
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-17 10:47:54 +08:00
|
|
|
|
public async Task<IRenderFilterNode> Instantiate(
|
|
|
|
|
RenderGroup group,
|
|
|
|
|
IEnumerable<(Renderer, Renderer)> proxyPairs,
|
2024-06-07 11:58:08 +08:00
|
|
|
|
ComputeContext context)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-23 09:09:51 +08:00
|
|
|
|
var node = new Node(group);
|
2024-06-07 11:58:08 +08:00
|
|
|
|
|
|
|
|
|
try
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-23 09:09:51 +08:00
|
|
|
|
await node.Init(proxyPairs, context);
|
2024-06-03 08:52:08 +08:00
|
|
|
|
}
|
2024-06-07 11:58:08 +08:00
|
|
|
|
catch (Exception e)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-07 11:58:08 +08:00
|
|
|
|
// dispose
|
|
|
|
|
throw;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
return node;
|
|
|
|
|
}
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
private class Node : IRenderFilterNode
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-23 09:09:51 +08:00
|
|
|
|
private readonly RenderGroup _group;
|
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
private Mesh _generatedMesh = null;
|
|
|
|
|
private ImmutableList<ModularAvatarShapeChanger> _changers;
|
2024-06-23 09:09:51 +08:00
|
|
|
|
private HashSet<int> _toDelete;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-23 09:09:51 +08:00
|
|
|
|
internal Node(RenderGroup group)
|
|
|
|
|
{
|
|
|
|
|
_group = group;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
private bool IsChangerActive(ModularAvatarShapeChanger changer, ComputeContext context)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-07 11:58:08 +08:00
|
|
|
|
if (changer == null) return false;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
if (context != null)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-07 11:58:08 +08:00
|
|
|
|
return context.ActiveAndEnabled(changer);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return changer.isActiveAndEnabled;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-23 09:09:51 +08:00
|
|
|
|
private HashSet<int> GetToDeleteSet(SkinnedMeshRenderer proxy, ComputeContext context)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-23 09:09:51 +08:00
|
|
|
|
_changers = _group.GetData<ImmutableList<ModularAvatarShapeChanger>>();
|
2024-06-07 11:58:08 +08:00
|
|
|
|
|
2024-06-23 09:09:51 +08:00
|
|
|
|
var toDelete = new HashSet<int>();
|
2024-07-02 11:38:27 +08:00
|
|
|
|
var mesh = context.Observe(proxy, p => p.sharedMesh, (a, b) =>
|
|
|
|
|
{
|
|
|
|
|
if (a != b)
|
|
|
|
|
{
|
|
|
|
|
Debug.Log($"mesh changed {a.GetInstanceID()} -> {b.GetInstanceID()}");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
});
|
2024-06-07 11:58:08 +08:00
|
|
|
|
|
|
|
|
|
foreach (var changer in _changers)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-07-02 11:38:27 +08:00
|
|
|
|
var shapes = context.Observe(changer, c => c.Shapes.ToImmutableList(), Enumerable.SequenceEqual);
|
2024-06-23 09:09:51 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
if (!IsChangerActive(changer, context)) continue;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-07-02 11:38:27 +08:00
|
|
|
|
foreach (var shape in shapes)
|
2024-06-07 11:58:08 +08:00
|
|
|
|
if (shape.ChangeType == ShapeChangeType.Delete)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-07 11:58:08 +08:00
|
|
|
|
var index = mesh.GetBlendShapeIndex(shape.ShapeName);
|
|
|
|
|
if (index < 0) continue;
|
|
|
|
|
toDelete.Add(index);
|
2024-06-03 08:52:08 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-23 09:09:51 +08:00
|
|
|
|
return toDelete;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task Init(
|
|
|
|
|
IEnumerable<(Renderer, Renderer)> renderers,
|
|
|
|
|
ComputeContext context
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
var (original, proxy) = renderers.First();
|
|
|
|
|
|
|
|
|
|
if (original == null || proxy == null) return;
|
|
|
|
|
if (!(proxy is SkinnedMeshRenderer smr)) return;
|
|
|
|
|
|
|
|
|
|
await Init(smr, context, GetToDeleteSet(smr, context));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<IRenderFilterNode> Refresh(IEnumerable<(Renderer, Renderer)> proxyPairs,
|
|
|
|
|
ComputeContext context, RenderAspects updatedAspects)
|
|
|
|
|
{
|
|
|
|
|
if ((updatedAspects & RenderAspects.Mesh) != 0) return null;
|
|
|
|
|
|
|
|
|
|
var (original, proxy) = proxyPairs.First();
|
|
|
|
|
|
|
|
|
|
if (original == null || proxy == null) return null;
|
|
|
|
|
if (!(proxy is SkinnedMeshRenderer smr)) return null;
|
|
|
|
|
|
|
|
|
|
var toDelete = GetToDeleteSet(smr, context);
|
|
|
|
|
if (toDelete.Count == _toDelete.Count && toDelete.All(_toDelete.Contains))
|
|
|
|
|
{
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var node = new Node(_group);
|
|
|
|
|
await node.Init(smr, context, toDelete);
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task Init(
|
|
|
|
|
SkinnedMeshRenderer proxy,
|
|
|
|
|
ComputeContext context,
|
|
|
|
|
HashSet<int> toDelete
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
_toDelete = toDelete;
|
|
|
|
|
var mesh = proxy.sharedMesh;
|
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
if (toDelete.Count > 0)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-07 11:58:08 +08:00
|
|
|
|
mesh = Object.Instantiate(mesh);
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
var bsPos = new Vector3[mesh.vertexCount];
|
|
|
|
|
bool[] targetVertex = new bool[mesh.vertexCount];
|
|
|
|
|
foreach (var bs in toDelete)
|
|
|
|
|
{
|
|
|
|
|
int frames = mesh.GetBlendShapeFrameCount(bs);
|
|
|
|
|
for (int f = 0; f < frames; f++)
|
|
|
|
|
{
|
|
|
|
|
mesh.GetBlendShapeFrameVertices(bs, f, bsPos, null, null);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < bsPos.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
if (bsPos[i].sqrMagnitude > 0.0001f)
|
|
|
|
|
{
|
|
|
|
|
targetVertex[i] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
List<int> tris = new List<int>();
|
|
|
|
|
for (int subMesh = 0; subMesh < mesh.subMeshCount; subMesh++)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-07 11:58:08 +08:00
|
|
|
|
tris.Clear();
|
|
|
|
|
|
|
|
|
|
var baseVertex = (int)mesh.GetBaseVertex(subMesh);
|
|
|
|
|
mesh.GetTriangles(tris, subMesh, false);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < tris.Count; i += 3)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-07 11:58:08 +08:00
|
|
|
|
if (targetVertex[tris[i] + baseVertex] || targetVertex[tris[i + 1] + baseVertex] ||
|
|
|
|
|
targetVertex[tris[i + 2] + baseVertex])
|
|
|
|
|
{
|
|
|
|
|
tris.RemoveRange(i, 3);
|
|
|
|
|
i -= 3;
|
|
|
|
|
}
|
2024-06-03 08:52:08 +08:00
|
|
|
|
}
|
2024-06-07 11:58:08 +08:00
|
|
|
|
|
|
|
|
|
mesh.SetTriangles(tris, subMesh, false, baseVertex: baseVertex);
|
2024-06-03 08:52:08 +08:00
|
|
|
|
}
|
2024-06-23 09:09:51 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
_generatedMesh = mesh;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-06-17 10:47:54 +08:00
|
|
|
|
public RenderAspects Reads => RenderAspects.Shapes | RenderAspects.Mesh;
|
|
|
|
|
public RenderAspects WhatChanged => RenderAspects.Shapes | RenderAspects.Mesh;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
if (_generatedMesh != null) Object.DestroyImmediate(_generatedMesh);
|
|
|
|
|
}
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
public void OnFrame(Renderer original, Renderer proxy)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-07 11:58:08 +08:00
|
|
|
|
if (_changers == null) return; // can happen transiently as we disable the last component
|
|
|
|
|
if (!(proxy is SkinnedMeshRenderer smr)) return;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
Mesh mesh;
|
|
|
|
|
if (_generatedMesh != null)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-07 11:58:08 +08:00
|
|
|
|
smr.sharedMesh = _generatedMesh;
|
|
|
|
|
mesh = _generatedMesh;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mesh = smr.sharedMesh;
|
|
|
|
|
}
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
if (mesh == null) return;
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
foreach (var changer in _changers)
|
|
|
|
|
{
|
|
|
|
|
if (!IsChangerActive(changer, null)) continue;
|
|
|
|
|
|
|
|
|
|
foreach (var shape in changer.Shapes)
|
2024-06-03 08:52:08 +08:00
|
|
|
|
{
|
2024-06-07 11:58:08 +08:00
|
|
|
|
var index = mesh.GetBlendShapeIndex(shape.ShapeName);
|
|
|
|
|
if (index < 0) continue;
|
|
|
|
|
|
|
|
|
|
float setToValue = -1;
|
|
|
|
|
|
|
|
|
|
switch (shape.ChangeType)
|
|
|
|
|
{
|
|
|
|
|
case ShapeChangeType.Delete:
|
|
|
|
|
setToValue = 100;
|
|
|
|
|
break;
|
|
|
|
|
case ShapeChangeType.Set:
|
|
|
|
|
setToValue = shape.Value;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-06-03 08:52:08 +08:00
|
|
|
|
|
2024-06-07 11:58:08 +08:00
|
|
|
|
smr.SetBlendShapeWeight(index, setToValue);
|
|
|
|
|
}
|
2024-06-03 08:52:08 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|