mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-09-13 22:39:39 -05:00
Merge pull request #1376 from notargs/feature/use_new_mesh_api
VRM1.0についてもNew Mesh APIを使う
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
@@ -8,7 +9,7 @@ namespace UniGLTF
|
||||
/// インターリーブされたメッシュの頂点情報を表す構造体
|
||||
/// そのままGPUにアップロードされる
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[Serializable, StructLayout(LayoutKind.Sequential)]
|
||||
internal readonly struct MeshVertex
|
||||
{
|
||||
private readonly Vector3 _position;
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public struct SkinJoints : IEquatable<SkinJoints>
|
||||
{
|
||||
public ushort Joint0;
|
||||
|
||||
120
Assets/VRM10/Runtime/IO/Model/CopyIndicesJob.cs
Normal file
120
Assets/VRM10/Runtime/IO/Model/CopyIndicesJob.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
|
||||
#if ENABLE_VRM10_BURST
|
||||
using Unity.Burst;
|
||||
#endif
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
/// <summary>
|
||||
/// インデックス配列を、オフセットを加えながら複製するJob郡
|
||||
/// MEMO: ushortを考慮することをやめればかなりシンプルに書ける
|
||||
/// </summary>
|
||||
internal struct CopyIndicesJobs
|
||||
{
|
||||
/// <summary>
|
||||
/// unsigned int -> unsigned int
|
||||
/// </summary>
|
||||
#if ENABLE_VRM10_BURST
|
||||
[BurstCompile]
|
||||
#endif
|
||||
public struct UInt2UInt : IJobParallelFor
|
||||
{
|
||||
private readonly uint _vertexOffset;
|
||||
|
||||
[ReadOnly] private readonly NativeSlice<uint> _source;
|
||||
[WriteOnly] private NativeSlice<uint> _destination;
|
||||
|
||||
public UInt2UInt(uint vertexOffset, NativeSlice<uint> source, NativeSlice<uint> destination)
|
||||
{
|
||||
_vertexOffset = vertexOffset;
|
||||
_source = source;
|
||||
_destination = destination;
|
||||
}
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
_destination[index] = _source[index] + _vertexOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// unsigned short -> unsigned int
|
||||
/// </summary>
|
||||
#if ENABLE_VRM10_BURST
|
||||
[BurstCompile]
|
||||
#endif
|
||||
public struct Ushort2Uint : IJobParallelFor
|
||||
{
|
||||
private readonly uint _vertexOffset;
|
||||
|
||||
[ReadOnly] private readonly NativeSlice<ushort> _source;
|
||||
[WriteOnly] private NativeSlice<uint> _destination;
|
||||
|
||||
public Ushort2Uint(uint vertexOffset, NativeSlice<ushort> source, NativeSlice<uint> destination)
|
||||
{
|
||||
_vertexOffset = vertexOffset;
|
||||
_source = source;
|
||||
_destination = destination;
|
||||
}
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
_destination[index] = _source[index] + _vertexOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// unsigned short -> unsigned short
|
||||
/// </summary>
|
||||
#if ENABLE_VRM10_BURST
|
||||
[BurstCompile]
|
||||
#endif
|
||||
public struct Ushort2Ushort : IJobParallelFor
|
||||
{
|
||||
private readonly ushort _vertexOffset;
|
||||
|
||||
[ReadOnly] private readonly NativeSlice<ushort> _source;
|
||||
[WriteOnly] private NativeSlice<ushort> _destination;
|
||||
|
||||
public Ushort2Ushort(ushort vertexOffset, NativeSlice<ushort> source, NativeSlice<ushort> destination)
|
||||
{
|
||||
_vertexOffset = vertexOffset;
|
||||
_source = source;
|
||||
_destination = destination;
|
||||
}
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
_destination[index] = (ushort)(_source[index] + _vertexOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// unsigned int -> unsigned short
|
||||
/// </summary>
|
||||
#if ENABLE_VRM10_BURST
|
||||
[BurstCompile]
|
||||
#endif
|
||||
public struct Uint2Ushort : IJobParallelFor
|
||||
{
|
||||
private readonly ushort _vertexOffset;
|
||||
|
||||
[ReadOnly] private readonly NativeSlice<uint> _source;
|
||||
[WriteOnly] private NativeSlice<ushort> _destination;
|
||||
|
||||
public Uint2Ushort(ushort vertexOffset, NativeSlice<uint> source, NativeSlice<ushort> destination)
|
||||
{
|
||||
_vertexOffset = vertexOffset;
|
||||
_source = source;
|
||||
_destination = destination;
|
||||
}
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
_destination[index] = (ushort)(_source[index] + _vertexOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/VRM10/Runtime/IO/Model/CopyIndicesJob.cs.meta
Normal file
3
Assets/VRM10/Runtime/IO/Model/CopyIndicesJob.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c6a7113ee13415e9b8c1063a683f3f8
|
||||
timeCreated: 1637033014
|
||||
79
Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs
Normal file
79
Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// 渡されたバッファを一つのバッファにインターリーブするJob
|
||||
/// </summary>
|
||||
#if ENABLE_VRM10_BURST
|
||||
[BurstCompile]
|
||||
#endif
|
||||
internal struct InterleaveMeshVerticesJob : IJobParallelFor
|
||||
{
|
||||
[WriteOnly]
|
||||
private NativeSlice<MeshVertex> _vertices;
|
||||
|
||||
[ReadOnly]
|
||||
private readonly NativeSlice<Vector3> _positions;
|
||||
|
||||
// default値を許容する
|
||||
[ReadOnly, NativeDisableContainerSafetyRestriction]
|
||||
private readonly NativeSlice<Vector3> _normals;
|
||||
|
||||
[ReadOnly, NativeDisableContainerSafetyRestriction]
|
||||
private readonly NativeSlice<Vector2> _texCoords;
|
||||
|
||||
[ReadOnly, NativeDisableContainerSafetyRestriction]
|
||||
private readonly NativeSlice<Color> _colors;
|
||||
|
||||
[ReadOnly, NativeDisableContainerSafetyRestriction]
|
||||
private readonly NativeSlice<Vector4> _weights;
|
||||
|
||||
[ReadOnly, NativeDisableContainerSafetyRestriction]
|
||||
private readonly NativeSlice<SkinJoints> _joints;
|
||||
|
||||
public InterleaveMeshVerticesJob(
|
||||
NativeSlice<MeshVertex> vertices,
|
||||
NativeSlice<Vector3> positions,
|
||||
NativeSlice<Vector3> normals = default,
|
||||
NativeSlice<Vector2> texCoords = default,
|
||||
NativeSlice<Color> colors = default,
|
||||
NativeSlice<Vector4> weights = default,
|
||||
NativeSlice<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.Length > 0 ? _normals[index] : Vector3.zero,
|
||||
_texCoords.Length > 0 ? _texCoords[index] : Vector2.zero,
|
||||
_colors.Length > 0 ? _colors[index] : Color.white,
|
||||
_joints.Length > 0 ? _joints[index].Joint0 : (ushort)0,
|
||||
_joints.Length > 0 ? _joints[index].Joint1 : (ushort)0,
|
||||
_joints.Length > 0 ? _joints[index].Joint2 : (ushort)0,
|
||||
_joints.Length > 0 ? _joints[index].Joint3 : (ushort)0,
|
||||
_weights.Length > 0 ? _weights[index].x : 0,
|
||||
_weights.Length > 0 ? _weights[index].y : 0,
|
||||
_weights.Length > 0 ? _weights[index].z : 0,
|
||||
_weights.Length > 0 ? _weights[index].w : 0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01d2fcab0a3941fa9cf2e4f1a641105b
|
||||
timeCreated: 1636688648
|
||||
@@ -1,6 +1,12 @@
|
||||
using System;
|
||||
using UniGLTF;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using UnityEngine.Rendering;
|
||||
using VrmLib;
|
||||
using Mesh = UnityEngine.Mesh;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
@@ -12,72 +18,100 @@ 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, Skin skin = null)
|
||||
{
|
||||
// submesh 方式
|
||||
var mesh = new UnityEngine.Mesh();
|
||||
if (src.IndexBuffer.Count > UInt16.MaxValue)
|
||||
{
|
||||
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
}
|
||||
Profiler.BeginSample("MeshImporter.LoadSharedMesh");
|
||||
var mesh = new Mesh();
|
||||
|
||||
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.AsNativeArray<Vector3>(Allocator.TempJob);
|
||||
var normals = src.VertexBuffer.Normals?.AsNativeArray<Vector3>(Allocator.TempJob) ?? default;
|
||||
var texCoords = src.VertexBuffer.TexCoords?.AsNativeArray<Vector2>(Allocator.TempJob) ?? default;
|
||||
var colors = src.VertexBuffer.Colors?.AsNativeArray<Color>(Allocator.TempJob) ?? default;
|
||||
var weights = src.VertexBuffer.Weights?.AsNativeArray<Vector4>(Allocator.TempJob) ?? default;
|
||||
var joints = src.VertexBuffer.Joints?.AsNativeArray<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);
|
||||
JobHandle.ScheduleBatchedJobs();
|
||||
|
||||
// BindPoseを更新
|
||||
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;
|
||||
}
|
||||
|
||||
mesh.subMeshCount = src.Submeshes.Count;
|
||||
var triangles = src.IndexBuffer.GetAsIntList();
|
||||
for (int i = 0; i < src.Submeshes.Count; ++i)
|
||||
// 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();
|
||||
|
||||
// Indexを更新
|
||||
switch (src.IndexBuffer.ComponentType)
|
||||
{
|
||||
var submesh = src.Submeshes[i];
|
||||
mesh.SetTriangles(triangles.GetRange(submesh.Offset, submesh.DrawCount), i);
|
||||
case AccessorValueType.UNSIGNED_SHORT:
|
||||
var shortIndices = src.IndexBuffer.AsNativeArray<ushort>(Allocator.Temp);
|
||||
mesh.SetIndexBufferParams(shortIndices.Length, IndexFormat.UInt16);
|
||||
mesh.SetIndexBufferData(shortIndices, 0, 0, shortIndices.Length);
|
||||
shortIndices.Dispose();
|
||||
break;
|
||||
case AccessorValueType.UNSIGNED_INT:
|
||||
var intIndices = src.IndexBuffer.AsNativeArray<uint>(Allocator.Temp);
|
||||
mesh.SetIndexBufferParams(intIndices.Length, IndexFormat.UInt32);
|
||||
mesh.SetIndexBufferData(intIndices, 0, 0, intIndices.Length);
|
||||
intIndices.Dispose();
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// SubMeshを更新
|
||||
mesh.subMeshCount = src.Submeshes.Count;
|
||||
for (var i = 0; i < src.Submeshes.Count; ++i)
|
||||
{
|
||||
var subMesh = src.Submeshes[i];
|
||||
mesh.SetSubMesh(i, new SubMeshDescriptor(subMesh.Offset, subMesh.DrawCount));
|
||||
}
|
||||
|
||||
// MorphTargetを更新
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,117 +1,289 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniGLTF;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using UnityEngine.Rendering;
|
||||
using VrmLib;
|
||||
using Mesh = UnityEngine.Mesh;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
public static class MeshImporterDivided
|
||||
{
|
||||
public static UnityEngine.Mesh LoadDivided(VrmLib.MeshGroup src)
|
||||
public static Mesh LoadDivided(MeshGroup meshGroup)
|
||||
{
|
||||
var dst = new UnityEngine.Mesh();
|
||||
if (src.Meshes.Sum(x => x.IndexBuffer.Count) > ushort.MaxValue)
|
||||
Profiler.BeginSample("MeshImporterDivided.LoadDivided");
|
||||
|
||||
var vertexCount = meshGroup.Meshes.Sum(mesh => mesh.VertexBuffer.Count);
|
||||
var indexCount = meshGroup.Meshes.Sum(mesh => mesh.IndexBuffer.Count);
|
||||
|
||||
var resultMesh = new Mesh();
|
||||
|
||||
// 頂点バッファ・BindPoseを構築して更新
|
||||
UpdateVerticesAndBindPose(meshGroup, vertexCount, resultMesh);
|
||||
|
||||
// インデックスバッファを構築して更新
|
||||
UpdateIndices(meshGroup, vertexCount, indexCount, resultMesh);
|
||||
|
||||
// SubMeshを更新
|
||||
resultMesh.subMeshCount = meshGroup.Meshes.Count;
|
||||
var indexOffset = 0;
|
||||
for (var i = 0; i < meshGroup.Meshes.Count; ++i)
|
||||
{
|
||||
dst.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
var mesh = meshGroup.Meshes[i];
|
||||
resultMesh.SetSubMesh(i, new SubMeshDescriptor(indexOffset, mesh.IndexBuffer.Count));
|
||||
indexOffset += mesh.IndexBuffer.Count;
|
||||
}
|
||||
|
||||
//
|
||||
// vertices
|
||||
//
|
||||
var vertexCount = src.Meshes.Sum(x => x.VertexBuffer.Count);
|
||||
var positions = new List<Vector3>(vertexCount);
|
||||
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)
|
||||
// 各種データを再構築
|
||||
resultMesh.RecalculateBounds();
|
||||
resultMesh.RecalculateTangents();
|
||||
|
||||
// BlendShapeを更新
|
||||
var blendShapeCount = meshGroup.Meshes[0].MorphTargets.Count;
|
||||
|
||||
for (var i = 0; i < blendShapeCount; ++i)
|
||||
{
|
||||
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)
|
||||
var positionsCount = 0;
|
||||
var normalsCount = 0;
|
||||
foreach (var mesh in meshGroup.Meshes)
|
||||
{
|
||||
var j = mesh.VertexBuffer.Joints.GetSpan<SkinJoints>();
|
||||
var w = mesh.VertexBuffer.Weights.GetSpan<Vector4>();
|
||||
for (int i = 0; i < mesh.VertexBuffer.Count; ++i)
|
||||
var morphTarget = mesh.MorphTargets[i];
|
||||
positionsCount += morphTarget.VertexBuffer.Positions.Count;
|
||||
normalsCount += morphTarget.VertexBuffer.Normals?.Count ?? morphTarget.VertexBuffer.Count;
|
||||
}
|
||||
|
||||
var blendShapePositions = new NativeArray<Vector3>(positionsCount, Allocator.Temp);
|
||||
var blendShapeNormals = new NativeArray<Vector3>(normalsCount, Allocator.Temp);
|
||||
|
||||
var blendShapePositionOffset = 0;
|
||||
var blendShapeNormalOffset = 0;
|
||||
foreach (var mesh in meshGroup.Meshes)
|
||||
{
|
||||
var morphTarget = mesh.MorphTargets[i];
|
||||
morphTarget.VertexBuffer.Positions.CopyToNativeSlice(
|
||||
new NativeSlice<Vector3>(
|
||||
blendShapePositions,
|
||||
blendShapePositionOffset,
|
||||
morphTarget.VertexBuffer.Positions.Count
|
||||
)
|
||||
);
|
||||
|
||||
// nullならdefault(0)のまま
|
||||
morphTarget.VertexBuffer.Normals?.CopyToNativeSlice(
|
||||
new NativeSlice<Vector3>(
|
||||
blendShapeNormals,
|
||||
blendShapeNormalOffset,
|
||||
morphTarget.VertexBuffer.Normals.Count
|
||||
)
|
||||
);
|
||||
|
||||
blendShapePositionOffset += morphTarget.VertexBuffer.Positions.Count;
|
||||
blendShapeNormalOffset += morphTarget.VertexBuffer.Normals?.Count ?? morphTarget.VertexBuffer.Count;
|
||||
}
|
||||
|
||||
resultMesh.AddBlendShapeFrame(meshGroup.Meshes[0].MorphTargets[i].Name,
|
||||
100.0f,
|
||||
blendShapePositions.ToArray(),
|
||||
blendShapeNormals.ToArray(),
|
||||
null);
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
|
||||
return resultMesh;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// インデックスバッファを更新する
|
||||
/// MEMO: 出力に対するushortを考慮することをやめればかなりシンプルに書ける
|
||||
/// </summary>
|
||||
private static void UpdateIndices(MeshGroup meshGroup, int vertexCount, int indexCount, Mesh resultMesh)
|
||||
{
|
||||
Profiler.BeginSample("MeshImporterDivided.UpdateIndices");
|
||||
|
||||
JobHandle jobHandle = default;
|
||||
|
||||
var disposables = new List<IDisposable>();
|
||||
|
||||
// 出力をushortにするべきかどうかを判別
|
||||
if (vertexCount < ushort.MaxValue)
|
||||
{
|
||||
var indices = new NativeArray<ushort>(indexCount, Allocator.TempJob);
|
||||
disposables.Add(indices);
|
||||
var indexOffset = 0;
|
||||
var vertexOffset = 0;
|
||||
foreach (var mesh in meshGroup.Meshes)
|
||||
{
|
||||
switch (mesh.IndexBuffer.ComponentType)
|
||||
{
|
||||
var jj = j[i];
|
||||
var ww = w[i];
|
||||
boneWeights.Add(new BoneWeight
|
||||
case AccessorValueType.SHORT:
|
||||
{
|
||||
boneIndex0 = jj.Joint0,
|
||||
boneIndex1 = jj.Joint1,
|
||||
boneIndex2 = jj.Joint2,
|
||||
boneIndex3 = jj.Joint3,
|
||||
weight0 = ww.x,
|
||||
weight1 = ww.y,
|
||||
weight2 = ww.z,
|
||||
weight3 = ww.w,
|
||||
});
|
||||
// unsigned short -> unsigned short
|
||||
var source = mesh.IndexBuffer.AsNativeArray<ushort>(Allocator.TempJob);
|
||||
disposables.Add(source);
|
||||
jobHandle = new CopyIndicesJobs.Ushort2Ushort(
|
||||
(ushort)vertexOffset,
|
||||
new NativeSlice<ushort>(source),
|
||||
new NativeSlice<ushort>(indices, indexOffset, mesh.IndexBuffer.Count))
|
||||
.Schedule(mesh.IndexBuffer.Count, 1, jobHandle);
|
||||
break;
|
||||
}
|
||||
case AccessorValueType.UNSIGNED_INT:
|
||||
{
|
||||
// unsigned int -> unsigned short
|
||||
var source = mesh.IndexBuffer.AsNativeArray<uint>(Allocator.TempJob);
|
||||
disposables.Add(source);
|
||||
jobHandle = new CopyIndicesJobs.Uint2Ushort(
|
||||
(ushort)vertexOffset,
|
||||
source,
|
||||
new NativeSlice<ushort>(indices, indexOffset, mesh.IndexBuffer.Count))
|
||||
.Schedule(mesh.IndexBuffer.Count, 1, jobHandle);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
vertexOffset += mesh.VertexBuffer.Count;
|
||||
indexOffset += mesh.IndexBuffer.Count;
|
||||
}
|
||||
|
||||
jobHandle.Complete();
|
||||
|
||||
resultMesh.SetIndexBufferParams(indexCount, IndexFormat.UInt16);
|
||||
resultMesh.SetIndexBufferData(indices, 0, 0, indexCount);
|
||||
}
|
||||
|
||||
dst.name = src.Name;
|
||||
dst.vertices = positions.ToArray();
|
||||
dst.normals = normals.ToArray();
|
||||
dst.uv = uv.ToArray();
|
||||
if (src.Skin != null)
|
||||
else
|
||||
{
|
||||
dst.boneWeights = boneWeights.ToArray();
|
||||
}
|
||||
|
||||
//
|
||||
// skin
|
||||
//
|
||||
if (src.Skin != null)
|
||||
{
|
||||
dst.bindposes = src.Skin.InverseMatrices.GetSpan<Matrix4x4>().ToArray();
|
||||
}
|
||||
|
||||
//
|
||||
// triangles
|
||||
//
|
||||
dst.subMeshCount = src.Meshes.Count;
|
||||
var offset = 0;
|
||||
for (int meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex)
|
||||
{
|
||||
var mesh = src.Meshes[meshIndex];
|
||||
var indices = mesh.IndexBuffer.GetAsIntArray().Select(x => offset + x).ToArray();
|
||||
dst.SetTriangles(indices, meshIndex);
|
||||
offset += mesh.VertexBuffer.Count;
|
||||
}
|
||||
|
||||
dst.RecalculateBounds();
|
||||
dst.RecalculateTangents();
|
||||
|
||||
//
|
||||
// blendshape
|
||||
//
|
||||
var blendShapeCount = src.Meshes[0].MorphTargets.Count;
|
||||
for (int 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)
|
||||
var indices = new NativeArray<uint>(indexCount, Allocator.TempJob);
|
||||
disposables.Add(indices);
|
||||
var indexOffset = 0;
|
||||
var vertexOffset = 0;
|
||||
foreach (var mesh in meshGroup.Meshes)
|
||||
{
|
||||
var morphTarget = src.Meshes[meshIndex].MorphTargets[i];
|
||||
positions.AddRange(morphTarget.VertexBuffer.Positions.GetSpan<Vector3>());
|
||||
if (morphTarget.VertexBuffer.Normals != null)
|
||||
switch (mesh.IndexBuffer.ComponentType)
|
||||
{
|
||||
normals.AddRange(morphTarget.VertexBuffer.Normals.GetSpan<Vector3>());
|
||||
}
|
||||
else
|
||||
{
|
||||
// fill zero
|
||||
normals.AddRange(Enumerable.Range(0, morphTarget.VertexBuffer.Count).Select(x => Vector3.zero));
|
||||
case AccessorValueType.SHORT:
|
||||
{
|
||||
// unsigned short -> unsigned int
|
||||
var source = mesh.IndexBuffer.AsNativeArray<ushort>(Allocator.TempJob);
|
||||
disposables.Add(source);
|
||||
jobHandle = new CopyIndicesJobs.Ushort2Uint(
|
||||
(uint)vertexOffset,
|
||||
source,
|
||||
new NativeSlice<uint>(indices, indexOffset, mesh.IndexBuffer.Count))
|
||||
.Schedule(mesh.IndexBuffer.Count, 1, jobHandle);
|
||||
break;
|
||||
}
|
||||
case AccessorValueType.UNSIGNED_INT:
|
||||
{
|
||||
// unsigned int -> unsigned int
|
||||
var source = mesh.IndexBuffer.AsNativeArray<uint>(Allocator.TempJob);
|
||||
disposables.Add(source);
|
||||
jobHandle = new CopyIndicesJobs.UInt2UInt(
|
||||
(uint)vertexOffset,
|
||||
source,
|
||||
new NativeSlice<uint>(indices, indexOffset, mesh.IndexBuffer.Count))
|
||||
.Schedule(mesh.IndexBuffer.Count, 1, jobHandle);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
vertexOffset += mesh.VertexBuffer.Count;
|
||||
indexOffset += mesh.IndexBuffer.Count;
|
||||
}
|
||||
dst.AddBlendShapeFrame(name, 100.0f, positions.ToArray(), normals.ToArray(), null);
|
||||
|
||||
jobHandle.Complete();
|
||||
|
||||
resultMesh.SetIndexBufferParams(indexCount, IndexFormat.UInt32);
|
||||
resultMesh.SetIndexBufferData(indices, 0, 0, indexCount);
|
||||
}
|
||||
|
||||
return dst;
|
||||
foreach (var disposable in disposables)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// メッシュの頂点情報の更新を行う際、MainThreadが空くため、その間にBindPoseの更新も行う
|
||||
/// </summary>
|
||||
private static void UpdateVerticesAndBindPose(
|
||||
MeshGroup meshGroup,
|
||||
int vertexCount,
|
||||
Mesh resultMesh)
|
||||
{
|
||||
Profiler.BeginSample("MeshImporterDivided.UpdateVerticesAndBindPose");
|
||||
|
||||
var disposables = new List<IDisposable>();
|
||||
|
||||
// JobのSchedule
|
||||
var vertices = new NativeArray<MeshVertex>(vertexCount, Allocator.TempJob);
|
||||
disposables.Add(vertices);
|
||||
|
||||
var indexOffset = 0;
|
||||
JobHandle interleaveVertexJob = default;
|
||||
|
||||
foreach (var mesh in meshGroup.Meshes)
|
||||
{
|
||||
var positions = mesh.VertexBuffer.Positions.AsNativeArray<Vector3>(Allocator.TempJob);
|
||||
var normals = mesh.VertexBuffer.Normals.AsNativeArray<Vector3>(Allocator.TempJob);
|
||||
var texCoords = mesh.VertexBuffer.TexCoords.AsNativeArray<Vector2>(Allocator.TempJob);
|
||||
var weights = meshGroup.Skin != null
|
||||
? mesh.VertexBuffer.Weights.AsNativeArray<Vector4>(Allocator.TempJob)
|
||||
: default;
|
||||
var joints = meshGroup.Skin != null
|
||||
? mesh.VertexBuffer.Joints.AsNativeArray<SkinJoints>(Allocator.TempJob)
|
||||
: default;
|
||||
if (positions.IsCreated) disposables.Add(positions);
|
||||
if (normals.IsCreated) disposables.Add(normals);
|
||||
if (texCoords.IsCreated) disposables.Add(texCoords);
|
||||
if (weights.IsCreated) disposables.Add(weights);
|
||||
if (joints.IsCreated) disposables.Add(joints);
|
||||
|
||||
interleaveVertexJob = new InterleaveMeshVerticesJob(
|
||||
new NativeSlice<MeshVertex>(vertices, indexOffset, mesh.VertexBuffer.Count),
|
||||
positions,
|
||||
normals,
|
||||
texCoords,
|
||||
default,
|
||||
weights,
|
||||
joints)
|
||||
.Schedule(mesh.VertexBuffer.Count, 1, interleaveVertexJob);
|
||||
indexOffset += mesh.VertexBuffer.Count;
|
||||
}
|
||||
|
||||
JobHandle.ScheduleBatchedJobs();
|
||||
|
||||
// 並行してBindposeの更新を行う
|
||||
if (meshGroup.Skin != null)
|
||||
{
|
||||
resultMesh.bindposes = meshGroup.Skin.InverseMatrices.GetSpan<Matrix4x4>().ToArray();
|
||||
}
|
||||
|
||||
// Jobを完了
|
||||
interleaveVertexJob.Complete();
|
||||
|
||||
// VertexBufferを設定
|
||||
MeshVertex.SetVertexBufferParamsToMesh(resultMesh, vertices.Length);
|
||||
resultMesh.SetVertexBufferData(vertices, 0, 0, vertices.Length);
|
||||
|
||||
// 各種バッファを破棄
|
||||
foreach (var disposable in disposables)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
Assets/VRM10/Runtime/IO/Model/MeshVertex.cs
Normal file
71
Assets/VRM10/Runtime/IO/Model/MeshVertex.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEditor.UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
/// <summary>
|
||||
/// インターリーブされたメッシュの頂点情報を表す構造体
|
||||
/// そのままGPUにアップロードされる
|
||||
/// </summary>
|
||||
[Serializable, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/VRM10/Runtime/IO/Model/MeshVertex.cs.meta
Normal file
3
Assets/VRM10/Runtime/IO/Model/MeshVertex.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78c70746c10e46bc97de0a7afedb23c7
|
||||
timeCreated: 1636625951
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,31 @@ namespace VrmLib
|
||||
return SpanLike.Wrap<T>(Bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// バッファをNativeArrayに変換して返す
|
||||
/// 開放の責務は使い手側にある点に注意
|
||||
/// </summary>
|
||||
public unsafe NativeArray<T> AsNativeArray<T>(Allocator allocator) where T : struct
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// バッファをNativeSliceへと書き込む
|
||||
/// </summary>
|
||||
public unsafe void CopyToNativeSlice<T>(NativeSlice<T> destArray) where T : unmanaged
|
||||
{
|
||||
fixed (byte* byteArray = Bytes.Array)
|
||||
{
|
||||
UnsafeUtility.MemCpy((T*)destArray.GetUnsafePtr(), byteArray + Bytes.Offset, Bytes.Count);
|
||||
}
|
||||
}
|
||||
|
||||
public void Assign<T>(T[] values) where T : struct
|
||||
{
|
||||
if (Marshal.SizeOf(typeof(T)) != Stride)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": false,
|
||||
|
||||
Reference in New Issue
Block a user