UniVRM/Assets/VRM10/vrmlib/Runtime/MeshMorphTargetSplitter.cs
ousttrue d53942a7bd merge vrm10
* MeshUtility を UniGLTF 下に移動
* Assets/VRM10 を追加
* JsonSchemaからのコード生成 UniGLTF/Editor/Generator を追加
2021-01-07 13:37:24 +09:00

266 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace VrmLib
{
class MeshSplitter
{
public static ValueTuple<
List<ValueTuple<int, Triangle>>,
List<ValueTuple<int, Triangle>>> SplitTriangles(Mesh src)
{
var triangles = src.Triangles.ToArray();
var morphUseCount = new int[triangles.Length];
// 各モーフ
foreach (var morph in src.MorphTargets)
{
if (morph.VertexBuffer.Count == 0)
{
// 無効
continue;
}
// POSITIONが使っているtriangleのカウンターをアップさせる
var positions = SpanLike.Wrap<Vector3>(morph.VertexBuffer.Positions.Bytes);
for (int i = 0; i < triangles.Length; ++i)
{
ref var triangle = ref triangles[i];
if (positions[triangle.Item2.Vertex0] != Vector3.Zero)
{
++morphUseCount[i];
}
if (positions[triangle.Item2.Vertex1] != Vector3.Zero)
{
++morphUseCount[i];
}
if (positions[triangle.Item2.Vertex2] != Vector3.Zero)
{
++morphUseCount[i];
}
}
}
var withTriangles = new List<ValueTuple<int, Triangle>>();
var withoutTriangles = new List<ValueTuple<int, Triangle>>();
for (int i = 0; i < triangles.Length; ++i)
{
if (morphUseCount[i] > 0)
{
// モーフで使われている
withTriangles.Add(triangles[i]);
}
else
{
// モーフで使われない
withoutTriangles.Add(triangles[i]);
}
}
return (withTriangles, withoutTriangles);
}
public static ValueTuple<
List<ValueTuple<int, Triangle>>,
List<ValueTuple<int, Triangle>>> SplitTrianglesByBoneIndices(Mesh src, HashSet<int> targetBoneIndices)
{
var triangles = src.Triangles.ToArray();
var isBodyMeshTriangle = new bool[triangles.Length];
var skinJointsSpan = SpanLike.Wrap<SkinJoints>(src.VertexBuffer.Joints.Bytes);
var boneWeightSpan = SpanLike.Wrap<Vector4>(src.VertexBuffer.Weights.Bytes);
for (int i = 0; i < triangles.Length; ++i)
{
ref var triangle = ref triangles[i];
var skinJoints0 = skinJointsSpan[triangle.Item2.Vertex0];
var boneWeights0 = boneWeightSpan[triangle.Item2.Vertex0];
if (boneWeights0.X > 0 && targetBoneIndices.Contains(skinJoints0.Joint0)) continue;
if (boneWeights0.Y > 0 && targetBoneIndices.Contains(skinJoints0.Joint1)) continue;
if (boneWeights0.Z > 0 && targetBoneIndices.Contains(skinJoints0.Joint2)) continue;
if (boneWeights0.W > 0 && targetBoneIndices.Contains(skinJoints0.Joint3)) continue;
var skinJoints1 = skinJointsSpan[triangle.Item2.Vertex1];
var boneWeights1 = boneWeightSpan[triangle.Item2.Vertex1];
if (boneWeights1.X > 0 && targetBoneIndices.Contains(skinJoints1.Joint0)) continue;
if (boneWeights1.Y > 0 && targetBoneIndices.Contains(skinJoints1.Joint1)) continue;
if (boneWeights1.Z > 0 && targetBoneIndices.Contains(skinJoints1.Joint2)) continue;
if (boneWeights1.W > 0 && targetBoneIndices.Contains(skinJoints1.Joint3)) continue;
var skinJoints2 = skinJointsSpan[triangle.Item2.Vertex2];
var boneWeights2 = boneWeightSpan[triangle.Item2.Vertex2];
if (boneWeights2.X > 0 && targetBoneIndices.Contains(skinJoints2.Joint0)) continue;
if (boneWeights2.Y > 0 && targetBoneIndices.Contains(skinJoints2.Joint1)) continue;
if (boneWeights2.Z > 0 && targetBoneIndices.Contains(skinJoints2.Joint2)) continue;
if (boneWeights2.W > 0 && targetBoneIndices.Contains(skinJoints2.Joint3)) continue;
isBodyMeshTriangle[i] = true;
}
var bodyTriangles = new List<ValueTuple<int, Triangle>>();
var headTriangles = new List<ValueTuple<int, Triangle>>();
for (int i = 0; i < triangles.Length; ++i)
{
if (isBodyMeshTriangle[i])
{
// 胴体メッシュ
bodyTriangles.Add(triangles[i]);
}
else
{
// 頭メッシュ
headTriangles.Add(triangles[i]);
}
}
return (headTriangles, bodyTriangles);
}
class VertexReorderMapper
{
public readonly List<int> IndexMap = new List<int>();
public BufferAccessor MapBuffer<T>(BufferAccessor srcBuffer) where T : struct
{
var src = SpanLike.Wrap<T>(srcBuffer.Bytes);
var dstBytes = new byte[srcBuffer.Stride * IndexMap.Count];
var dst = SpanLike.Wrap<T>(new ArraySegment<byte>(dstBytes));
for (int i = 0; i < IndexMap.Count; ++i)
{
dst[i] = src[IndexMap[i]];
}
return new BufferAccessor(new ArraySegment<byte>(dstBytes), srcBuffer.ComponentType, srcBuffer.AccessorType, IndexMap.Count);
}
public VertexBuffer Map(VertexBuffer src)
{
var dst = new VertexBuffer();
foreach (var (semantic, buffer) in src)
{
BufferAccessor mapped = null;
if (buffer.ComponentType == AccessorValueType.FLOAT)
{
switch (buffer.AccessorType)
{
case AccessorVectorType.VEC2:
mapped = MapBuffer<Vector2>(buffer);
break;
case AccessorVectorType.VEC3:
mapped = MapBuffer<Vector3>(buffer);
break;
case AccessorVectorType.VEC4:
mapped = MapBuffer<Vector4>(buffer);
break;
default:
throw new NotImplementedException();
}
}
else if (buffer.ComponentType == AccessorValueType.UNSIGNED_SHORT)
{
if (buffer.AccessorType == AccessorVectorType.VEC4)
{
mapped = MapBuffer<SkinJoints>(buffer);
}
else
{
throw new NotImplementedException();
}
}
else
{
throw new NotImplementedException();
}
dst.Add(semantic, mapped);
}
return dst;
}
public readonly List<int> Indices = new List<int>();
public ArraySegment<byte> IndexBytes
{
get
{
var bytes = new byte[Indices.Count * 4];
var span = SpanLike.Wrap<Int32>(new ArraySegment<byte>(bytes));
for (int i = 0; i < span.Length; ++i)
{
span[i] = Indices[i];
}
return new ArraySegment<byte>(bytes);
}
}
public BufferAccessor CreateIndexBuffer()
{
return new BufferAccessor(IndexBytes,
AccessorValueType.UNSIGNED_INT, AccessorVectorType.SCALAR, Indices.Count);
}
void PushIndex(int src)
{
int index = IndexMap.IndexOf(src);
if (index == -1)
{
// index to src map
index = IndexMap.Count;
IndexMap.Add(src);
}
Indices.Add(index);
}
public VertexReorderMapper(IEnumerable<ValueTuple<int, Triangle>> submeshTriangles)
{
foreach (var t in submeshTriangles)
{
PushIndex(t.Item2.Vertex0);
PushIndex(t.Item2.Vertex1);
PushIndex(t.Item2.Vertex2);
}
}
}
public static Mesh SeparateMesh(Mesh src, IEnumerable<ValueTuple<int, Triangle>> submeshTriangles,
bool includeMorphTarget = false)
{
var mapper = new VertexReorderMapper(submeshTriangles);
var mesh = new Mesh
{
Topology = TopologyType.Triangles,
IndexBuffer = mapper.CreateIndexBuffer(),
VertexBuffer = mapper.Map(src.VertexBuffer),
};
var offset = 0;
foreach (var triangles in submeshTriangles.GroupBy(x => x.Item1, x => x).OrderBy(x => x.Key))
{
var count = triangles.Count() * 3;
mesh.Submeshes.Add(new Submesh(offset, count, src.Submeshes[triangles.Key].Material));
offset += count;
}
if (includeMorphTarget)
{
foreach (var target in src.MorphTargets)
{
mesh.MorphTargets.Add(new MorphTarget(target.Name)
{
VertexBuffer = mapper.Map(target.VertexBuffer)
});
}
}
return mesh;
}
}
}