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

282 lines
7.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace VrmLib
{
public enum TopologyType : int
{
Points = 0,
Lines = 1,
LineLoop = 2,
LineStrip = 3,
Triangles = 4,
TriangleStrip = 5,
TriangleFan = 6,
}
public struct Triangle
{
public int Vertex0;
public int Vertex1;
public int Vertex2;
public Triangle(int v0, int v1, int v2)
{
Vertex0 = v0;
Vertex1 = v1;
Vertex2 = v2;
}
}
public class Submesh
{
public int Offset;
public int DrawCount;
public Material Material;
public override string ToString()
{
return $"{Material.Name}({DrawCount})";
}
public Submesh(Material material) : this(0, 0, material)
{
}
public Submesh(int offset, int drawCount, Material material)
{
Offset = offset;
DrawCount = drawCount;
Material = material;
}
}
public class Mesh
{
public VertexBuffer VertexBuffer;
public BufferAccessor IndexBuffer;
/// <summary>
/// indicesの最大値が65535未満(-1を避ける)ならばushort 型で、
/// そうでなければ int型で IndexBufferを代入する
/// </summary>
public void AssignIndexBuffer(SpanLike<int> indices)
{
bool isInt = false;
foreach (var i in indices)
{
if (i >= short.MaxValue)
{
isInt = true;
break;
}
}
if (isInt)
{
if (IndexBuffer.Stride != 4)
{
IndexBuffer.ComponentType = AccessorValueType.UNSIGNED_INT;
if (IndexBuffer.AccessorType != AccessorVectorType.SCALAR)
{
throw new Exception();
}
}
// 変換なし
IndexBuffer.Assign(indices);
}
else
{
// int to ushort
IndexBuffer.AssignAsShort(indices);
}
}
public TopologyType Topology = TopologyType.Triangles;
public List<Submesh> Submeshes { private set; get; } = new List<Submesh>();
public int SubmeshTotalDrawCount => Submeshes.Sum(x => x.DrawCount);
public IEnumerable<Triangle> GetTriangles(int i)
{
var indices = IndexBuffer.GetAsIntArray();
var submesh = Submeshes[i];
var submeshEnd = submesh.Offset + submesh.DrawCount;
for (int j = submesh.Offset; j < submeshEnd; j += 3)
{
var triangle = new Triangle(
indices[j],
indices[j + 1],
indices[j + 2]
);
yield return triangle;
}
}
public IEnumerable<ValueTuple<int, Triangle>> Triangles
{
get
{
if (Topology != TopologyType.Triangles)
{
throw new InvalidOperationException();
}
var indices = IndexBuffer.GetAsIntArray();
for (int i = 0; i < Submeshes.Count; ++i)
{
var submesh = Submeshes[i];
var submeshEnd = submesh.Offset + submesh.DrawCount;
for (int j = submesh.Offset; j < submeshEnd; j += 3)
{
var triangle = new Triangle(
indices[j],
indices[j + 1],
indices[j + 2]
);
yield return (i, triangle);
}
}
}
}
bool GetSubmeshOverlapped<T>() where T : struct
{
var indices = IndexBuffer.GetSpan<ushort>();
var offset = 0;
var max = 0;
foreach (var x in Submeshes)
{
var submeshIndices = indices.Slice(offset, x.DrawCount);
var currentMax = 0;
foreach (var y in submeshIndices)
{
if (y < max)
{
return true;
}
currentMax = Math.Max(y, currentMax);
}
offset += x.DrawCount;
max = currentMax;
}
return false;
}
public bool IsSubmeshOverlapped
{
get
{
if (Submeshes.Count <= 1)
{
return false;
}
switch (IndexBuffer.ComponentType)
{
case AccessorValueType.UNSIGNED_SHORT:
return GetSubmeshOverlapped<ushort>();
case AccessorValueType.UNSIGNED_INT:
return GetSubmeshOverlapped<uint>();
default:
throw new NotImplementedException();
}
}
}
public List<MorphTarget> MorphTargets = new List<MorphTarget>();
public override string ToString()
{
var sb = new System.Text.StringBuilder();
foreach (var key in VertexBuffer.Select(x => x.Key))
{
sb.Append($"[{key}]");
}
if (IndexBuffer != null)
{
switch (Topology)
{
case TopologyType.Triangles:
sb.Append($" {IndexBuffer.Count / 3} tris");
break;
default:
sb.Append($" topology: {Topology}");
break;
}
}
if (MorphTargets.Any())
{
sb.Append($", {MorphTargets.Count} morphs");
foreach (var kv in MorphTargets[0].VertexBuffer)
{
sb.Append($"[{kv.Key}]");
}
}
var byteLength = VertexBuffer.ByteLength + IndexBuffer.ByteLength + MorphTargets.Sum(x => x.VertexBuffer.ByteLength);
sb.Append($": expected {byteLength / 1000 / 1000} MB");
return sb.ToString();
}
public Mesh(TopologyType topology = TopologyType.Triangles)
{
Topology = topology;
}
public void RemoveUnusedSubmesh()
{
Submeshes = Submeshes.Where(x => x.DrawCount != 0).ToList();
}
}
public class MeshGroup: GltfId
{
public readonly string Name;
public readonly List<Mesh> Meshes = new List<Mesh>();
public Skin Skin;
public override string ToString()
{
var sb = new StringBuilder();
sb.Append(Name);
if (Skin != null)
{
sb.Append("(skinned)");
}
var isFirst = true;
foreach (var mesh in Meshes)
{
if (isFirst)
{
isFirst = false;
}
else
{
sb.Append(", ");
}
sb.Append(mesh);
}
return sb.ToString();
}
public MeshGroup(string name)
{
Name = name;
}
}
}