UniVRM10のVertexの更新をNew Mesh APIに変更

This commit is contained in:
notargs
2021-11-12 13:25:03 +09:00
parent b41e5e8252
commit a3674a992b
10 changed files with 247 additions and 60 deletions

View File

@@ -2,6 +2,7 @@ using System;
namespace UniGLTF
{
[Serializable]
public struct SkinJoints : IEquatable<SkinJoints>
{
public ushort Joint0;

View File

@@ -0,0 +1,65 @@
using UniGLTF;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine;
#if ENABLE_VRM10_BURST
using Unity.Burst;
#endif
namespace UniVRM10
{
#if ENABLE_VRM10_BURST
[BurstCompile]
#endif
internal struct InterleaveMeshVerticesJob : IJobParallelFor
{
[WriteOnly] private NativeArray<MeshVertex> _vertices;
[ReadOnly] private readonly NativeArray<Vector3> _positions;
// default値を許容する
[ReadOnly, NativeDisableContainerSafetyRestriction] private readonly NativeArray<Vector3> _normals;
[ReadOnly, NativeDisableContainerSafetyRestriction] private readonly NativeArray<Vector2> _texCoords;
[ReadOnly, NativeDisableContainerSafetyRestriction] private readonly NativeArray<Color> _colors;
[ReadOnly, NativeDisableContainerSafetyRestriction] private readonly NativeArray<Vector4> _weights;
[ReadOnly, NativeDisableContainerSafetyRestriction] private readonly NativeArray<SkinJoints> _joints;
public InterleaveMeshVerticesJob(
NativeArray<MeshVertex> vertices,
NativeArray<Vector3> positions,
NativeArray<Vector3> normals = default,
NativeArray<Vector2> texCoords = default,
NativeArray<Color> colors = default,
NativeArray<Vector4> weights = default,
NativeArray<SkinJoints> joints = default)
{
_vertices = vertices;
_positions = positions;
_normals = normals;
_texCoords = texCoords;
_colors = colors;
_weights = weights;
_joints = joints;
}
public void Execute(int index)
{
_vertices[index] = new MeshVertex(
_positions[index],
_normals.IsCreated ? _normals[index] : Vector3.zero,
_texCoords.IsCreated ? _texCoords[index] : Vector2.zero,
_colors.IsCreated ? _colors[index] : Color.white,
_joints.IsCreated ? _joints[index].Joint0 : (ushort)0,
_joints.IsCreated ? _joints[index].Joint1 : (ushort)0,
_joints.IsCreated ? _joints[index].Joint2 : (ushort)0,
_joints.IsCreated ? _joints[index].Joint3 : (ushort)0,
_weights.IsCreated ? _weights[index].x : 0,
_weights.IsCreated ? _weights[index].y : 0,
_weights.IsCreated ? _weights[index].z : 0,
_weights.IsCreated ? _weights[index].w : 0
);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 01d2fcab0a3941fa9cf2e4f1a641105b
timeCreated: 1636688648

View File

@@ -1,6 +1,9 @@
using System;
using UniGLTF;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Profiling;
namespace UniVRM10
{
@@ -12,55 +15,63 @@ namespace UniVRM10
/// <param name="mesh"></param>
/// <param name="src"></param>
/// <param name="skin"></param>
public static UnityEngine.Mesh LoadSharedMesh(VrmLib.Mesh src, VrmLib.Skin skin = null)
public static Mesh LoadSharedMesh(VrmLib.Mesh src, VrmLib.Skin skin = null)
{
// submesh 方式
var mesh = new UnityEngine.Mesh();
if (src.IndexBuffer.Count > UInt16.MaxValue)
Profiler.BeginSample("MeshImporter.LoadSharedMesh");
var mesh = new Mesh();
if (src.IndexBuffer.Count > ushort.MaxValue)
{
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
}
mesh.vertices = src.VertexBuffer.Positions.GetSpan<Vector3>().ToArray();
mesh.normals = src.VertexBuffer.Normals?.GetSpan<Vector3>().ToArray();
mesh.uv = src.VertexBuffer.TexCoords?.GetSpan<Vector2>().ToArray();
mesh.colors = src.VertexBuffer.Colors?.GetSpan<Color>().ToArray();
if (src.VertexBuffer.Weights != null && src.VertexBuffer.Joints != null)
var positions = src.VertexBuffer.Positions.GetNativeArray<Vector3>(Allocator.TempJob);
var normals = src.VertexBuffer.Normals?.GetNativeArray<Vector3>(Allocator.TempJob) ?? default;
var texCoords = src.VertexBuffer.TexCoords?.GetNativeArray<Vector2>(Allocator.TempJob) ?? default;
var colors = src.VertexBuffer.Colors?.GetNativeArray<Color>(Allocator.TempJob) ?? default;
var weights = src.VertexBuffer.Weights?.GetNativeArray<Vector4>(Allocator.TempJob) ?? default;
var joints = src.VertexBuffer.Joints?.GetNativeArray<SkinJoints>(Allocator.TempJob) ?? default;
var vertices = new NativeArray<MeshVertex>(positions.Length, Allocator.TempJob);
// JobとBindPoseの更新を並行して行う
var jobHandle =
new InterleaveMeshVerticesJob(vertices, positions, normals, texCoords, colors, weights, joints)
.Schedule(vertices.Length, 1);
if (weights.IsCreated && joints.IsCreated)
{
var boneWeights = new BoneWeight[mesh.vertexCount];
if (src.VertexBuffer.Weights.Count != mesh.vertexCount || src.VertexBuffer.Joints.Count != mesh.vertexCount)
if (weights.Length != positions.Length || joints.Length != positions.Length)
{
throw new ArgumentException();
}
var weights = src.VertexBuffer.Weights.GetSpan<Vector4>();
var joints = src.VertexBuffer.Joints.GetSpan<SkinJoints>();
if (skin != null)
{
mesh.bindposes = skin.InverseMatrices.GetSpan<Matrix4x4>().ToArray();
}
for (int i = 0; i < weights.Length; ++i)
{
var w = weights[i];
boneWeights[i].weight0 = w.x;
boneWeights[i].weight1 = w.y;
boneWeights[i].weight2 = w.z;
boneWeights[i].weight3 = w.w;
}
for (int i = 0; i < joints.Length; ++i)
{
var j = joints[i];
boneWeights[i].boneIndex0 = j.Joint0;
boneWeights[i].boneIndex1 = j.Joint1;
boneWeights[i].boneIndex2 = j.Joint2;
boneWeights[i].boneIndex3 = j.Joint3;
}
mesh.boneWeights = boneWeights;
}
// Jobを完了
jobHandle.Complete();
// 入力のNativeArrayを開放
positions.Dispose();
if (normals.IsCreated) normals.Dispose();
if (texCoords.IsCreated) texCoords.Dispose();
if (colors.IsCreated) colors.Dispose();
if (weights.IsCreated) weights.Dispose();
if (joints.IsCreated) joints.Dispose();
// 頂点を更新
MeshVertex.SetVertexBufferParamsToMesh(mesh, vertices.Length);
mesh.SetVertexBufferData(vertices, 0, 0, vertices.Length);
// 出力のNativeArrayを開放
vertices.Dispose();
// submesh 方式
mesh.subMeshCount = src.Submeshes.Count;
var triangles = src.IndexBuffer.GetAsIntList();
for (int i = 0; i < src.Submeshes.Count; ++i)
for (var i = 0; i < src.Submeshes.Count; ++i)
{
var submesh = src.Submeshes[i];
mesh.SetTriangles(triangles.GetRange(submesh.Offset, submesh.DrawCount), i);
@@ -68,16 +79,18 @@ namespace UniVRM10
foreach (var morphTarget in src.MorphTargets)
{
var positions =
var morphTargetPositions =
morphTarget.VertexBuffer.Positions != null
? morphTarget.VertexBuffer.Positions.GetSpan<Vector3>().ToArray()
: new Vector3[mesh.vertexCount] // dummy
;
mesh.AddBlendShapeFrame(morphTarget.Name, 100.0f, positions, null, null);
mesh.AddBlendShapeFrame(morphTarget.Name, 100.0f, morphTargetPositions, null, null);
}
mesh.RecalculateBounds();
mesh.RecalculateTangents();
Profiler.EndSample();
return mesh;
}

View File

@@ -2,6 +2,8 @@ using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
using UnityEngine.Profiling;
using Mesh = VrmLib.Mesh;
namespace UniVRM10
{
@@ -9,6 +11,8 @@ namespace UniVRM10
{
public static UnityEngine.Mesh LoadDivided(VrmLib.MeshGroup src)
{
Profiler.BeginSample("MeshImporterDivided.LoadDivided");
var dst = new UnityEngine.Mesh();
if (src.Meshes.Sum(x => x.IndexBuffer.Count) > ushort.MaxValue)
{
@@ -23,32 +27,30 @@ namespace UniVRM10
var normals = new List<Vector3>(vertexCount);
var uv = new List<Vector2>(vertexCount);
var boneWeights = new List<BoneWeight>(vertexCount);
for (int meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex)
foreach (var mesh in src.Meshes)
{
var mesh = src.Meshes[meshIndex];
positions.AddRange(mesh.VertexBuffer.Positions.GetSpan<Vector3>());
normals.AddRange(mesh.VertexBuffer.Normals.GetSpan<Vector3>());
uv.AddRange(mesh.VertexBuffer.TexCoords.GetSpan<Vector2>());
if (src.Skin != null)
if (src.Skin == null) continue;
var joints = mesh.VertexBuffer.Joints.GetSpan<SkinJoints>();
var weights = mesh.VertexBuffer.Weights.GetSpan<Vector4>();
for (var i = 0; i < mesh.VertexBuffer.Count; ++i)
{
var j = mesh.VertexBuffer.Joints.GetSpan<SkinJoints>();
var w = mesh.VertexBuffer.Weights.GetSpan<Vector4>();
for (int i = 0; i < mesh.VertexBuffer.Count; ++i)
var joint = joints[i];
var weight = weights[i];
boneWeights.Add(new BoneWeight
{
var jj = j[i];
var ww = w[i];
boneWeights.Add(new BoneWeight
{
boneIndex0 = jj.Joint0,
boneIndex1 = jj.Joint1,
boneIndex2 = jj.Joint2,
boneIndex3 = jj.Joint3,
weight0 = ww.x,
weight1 = ww.y,
weight2 = ww.z,
weight3 = ww.w,
});
}
boneIndex0 = joint.Joint0,
boneIndex1 = joint.Joint1,
boneIndex2 = joint.Joint2,
boneIndex3 = joint.Joint3,
weight0 = weight.x,
weight1 = weight.y,
weight2 = weight.z,
weight3 = weight.w,
});
}
}
@@ -74,7 +76,7 @@ namespace UniVRM10
//
dst.subMeshCount = src.Meshes.Count;
var offset = 0;
for (int meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex)
for (var meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex)
{
var mesh = src.Meshes[meshIndex];
var indices = mesh.IndexBuffer.GetAsIntArray().Select(x => offset + x).ToArray();
@@ -89,12 +91,12 @@ namespace UniVRM10
// blendshape
//
var blendShapeCount = src.Meshes[0].MorphTargets.Count;
for (int i = 0; i < blendShapeCount; ++i)
for (var i = 0; i < blendShapeCount; ++i)
{
positions.Clear();
normals.Clear();
var name = src.Meshes[0].MorphTargets[i].Name;
for (int meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex)
for (var meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex)
{
var morphTarget = src.Meshes[meshIndex].MorphTargets[i];
positions.AddRange(morphTarget.VertexBuffer.Positions.GetSpan<Vector3>());
@@ -111,6 +113,8 @@ namespace UniVRM10
dst.AddBlendShapeFrame(name, 100.0f, positions.ToArray(), normals.ToArray(), null);
}
Profiler.EndSample();
return dst;
}
}

View File

@@ -0,0 +1,70 @@
using System.Runtime.InteropServices;
using UnityEditor.UI;
using UnityEngine;
using UnityEngine.Rendering;
namespace UniVRM10
{
/// <summary>
/// インターリーブされたメッシュの頂点情報を表す構造体
/// そのままGPUにアップロードされる
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal readonly struct MeshVertex
{
private readonly Vector3 _position;
private readonly Vector3 _normal;
private readonly Color _color;
private readonly Vector2 _texCoord;
private readonly float _boneWeight0;
private readonly float _boneWeight1;
private readonly float _boneWeight2;
private readonly float _boneWeight3;
private readonly ushort _boneIndex0;
private readonly ushort _boneIndex1;
private readonly ushort _boneIndex2;
private readonly ushort _boneIndex3;
public MeshVertex(
Vector3 position,
Vector3 normal,
Vector2 texCoord,
Color color,
ushort boneIndex0,
ushort boneIndex1,
ushort boneIndex2,
ushort boneIndex3,
float boneWeight0,
float boneWeight1,
float boneWeight2,
float boneWeight3)
{
_position = position;
_normal = normal;
_texCoord = texCoord;
_color = color;
_boneIndex0 = boneIndex0;
_boneIndex1 = boneIndex1;
_boneIndex2 = boneIndex2;
_boneIndex3 = boneIndex3;
_boneWeight0 = boneWeight0;
_boneWeight1 = boneWeight1;
_boneWeight2 = boneWeight2;
_boneWeight3 = boneWeight3;
}
private static readonly VertexAttributeDescriptor[] vertexAttributeDescriptor = {
new VertexAttributeDescriptor(VertexAttribute.Position),
new VertexAttributeDescriptor(VertexAttribute.Normal),
new VertexAttributeDescriptor(VertexAttribute.Color, dimension: 4),
new VertexAttributeDescriptor(VertexAttribute.TexCoord0, dimension: 2),
new VertexAttributeDescriptor(VertexAttribute.BlendWeight, dimension: 4),
new VertexAttributeDescriptor(VertexAttribute.BlendIndices, VertexAttributeFormat.UInt16, 4),
};
public static void SetVertexBufferParamsToMesh(Mesh mesh, int length)
{
mesh.SetVertexBufferParams(length, vertexAttributeDescriptor);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 78c70746c10e46bc97de0a7afedb23c7
timeCreated: 1636625951

View File

@@ -8,7 +8,8 @@
"GUID:bce005214fa49654d93927908c15b1f2",
"GUID:0aaf403bd13871a44b7127aef2695ff8",
"GUID:b7aa47b240b57de44a4b2021c143c9bf",
"GUID:f2ca1407928ebdc4bbe7765cc278be44"
"GUID:f2ca1407928ebdc4bbe7765cc278be44",
"GUID:2665a8d13d1b3f18800f46e256720795"
],
"includePlatforms": [],
"excludePlatforms": [],
@@ -17,6 +18,12 @@
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"versionDefines": [
{
"name": "com.unity.burst",
"expression": "0.0.1",
"define": "ENABLE_VRM10_BURST"
}
],
"noEngineReferences": false
}

View File

@@ -4,6 +4,8 @@ using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using UniGLTF;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace VrmLib
{
@@ -103,6 +105,25 @@ namespace VrmLib
return SpanLike.Wrap<T>(Bytes);
}
/// <summary>
/// バッファをNativeArrayに変換して返す
/// 開放の責務は使い手側にある点に注意
/// </summary>
public unsafe NativeArray<T> GetNativeArray<T>(Allocator allocator, bool checkStride = true) where T : struct
{
if (checkStride && Marshal.SizeOf(typeof(T)) != Stride)
{
throw new Exception("different sizeof(T) with stride");
}
fixed (byte* byteArray = Bytes.Array)
{
var nativeArray = new NativeArray<T>(Bytes.Count / Marshal.SizeOf<T>(), allocator);
UnsafeUtility.MemCpy(nativeArray.GetUnsafePtr(), byteArray + Bytes.Offset, Bytes.Count);
return nativeArray;
}
}
public void Assign<T>(T[] values) where T : struct
{
if (Marshal.SizeOf(typeof(T)) != Stride)

View File

@@ -5,7 +5,7 @@
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,