mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-09-08 03:48:19 -05:00
Merge pull request #861 from ousttrue/fix/mesh_export_logic
VertexBufferをSubMeshで分割してエクスポートするオプション
This commit is contained in:
@@ -14,5 +14,7 @@ namespace UniGLTF
|
||||
public bool Sparse;
|
||||
|
||||
public bool DropNormal;
|
||||
|
||||
public bool DivideVertexBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace UniGLTF
|
||||
using (var exporter = new gltfExporter(gltf, inverseAxis))
|
||||
{
|
||||
exporter.Prepare(go);
|
||||
exporter.Export(settings, AssetTextureUtil.IsTextureEditorAsset );
|
||||
exporter.Export(settings, AssetTextureUtil.IsTextureEditorAsset);
|
||||
}
|
||||
|
||||
|
||||
@@ -244,6 +244,7 @@ namespace UniGLTF
|
||||
{
|
||||
ExportOnlyBlendShapePosition = settings.DropNormal,
|
||||
UseSparseAccessorForMorphTarget = settings.Sparse,
|
||||
DivideVertexBuffer = settings.DivideVertexBuffer,
|
||||
}, settings.InverseAxis);
|
||||
}
|
||||
}
|
||||
|
||||
35
Assets/UniGLTF/Runtime/UniGLTF/IO/MeshExportSettings.cs
Normal file
35
Assets/UniGLTF/Runtime/UniGLTF/IO/MeshExportSettings.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public struct MeshExportSettings
|
||||
{
|
||||
//
|
||||
// https://github.com/vrm-c/UniVRM/issues/800
|
||||
//
|
||||
// VertexBuffer を共有バッファ方式にする
|
||||
// UniVRM-0.71.0 までの挙動
|
||||
//
|
||||
public bool DivideVertexBuffer;
|
||||
|
||||
// MorphTarget に Sparse Accessor を使う
|
||||
public bool UseSparseAccessorForMorphTarget;
|
||||
|
||||
// MorphTarget を Position だけにする(normal とか捨てる)
|
||||
public bool ExportOnlyBlendShapePosition;
|
||||
|
||||
// tangent を出力する
|
||||
public bool ExportTangents;
|
||||
|
||||
public static MeshExportSettings Default => new MeshExportSettings
|
||||
{
|
||||
UseSparseAccessorForMorphTarget = false,
|
||||
ExportOnlyBlendShapePosition = false,
|
||||
DivideVertexBuffer = false,
|
||||
#if GLTF_EXPORT_TANGENTS
|
||||
ExportTangents = true,
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Runtime/UniGLTF/IO/MeshExportSettings.cs.meta
Normal file
11
Assets/UniGLTF/Runtime/UniGLTF/IO/MeshExportSettings.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26b97430e1698e94e9a27ab322f3eee5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -3,155 +3,28 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Serializable]
|
||||
public struct MeshExportSettings
|
||||
{
|
||||
// MorphTarget に Sparse Accessor を使う
|
||||
public bool UseSparseAccessorForMorphTarget;
|
||||
|
||||
// MorphTarget を Position だけにする(normal とか捨てる)
|
||||
public bool ExportOnlyBlendShapePosition;
|
||||
|
||||
public static MeshExportSettings Default => new MeshExportSettings
|
||||
{
|
||||
UseSparseAccessorForMorphTarget = false,
|
||||
ExportOnlyBlendShapePosition = false,
|
||||
};
|
||||
}
|
||||
|
||||
public static class MeshExporter
|
||||
{
|
||||
static glTFMesh ExportPrimitives(glTF gltf, int bufferIndex, Mesh mesh, Material[] meshMaterials, BoneWeight[] boneWeights, int[] jointIndexMap,
|
||||
List<Material> exportedMaterials)
|
||||
{
|
||||
var positions = mesh.vertices.Select(y => y.ReverseZ()).ToArray();
|
||||
var positionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, positions, glBufferTarget.ARRAY_BUFFER);
|
||||
gltf.accessors[positionAccessorIndex].min = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Min(a.x, b.x), Math.Min(a.y, b.y), Mathf.Min(a.z, b.z))).ToArray();
|
||||
gltf.accessors[positionAccessorIndex].max = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray();
|
||||
|
||||
var normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.normals.Select(y => y.normalized.ReverseZ()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
#if GLTF_EXPORT_TANGENTS
|
||||
var tangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.tangents.Select(y => y.ReverseZ()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
#endif
|
||||
var uvAccessorIndex0 = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.uv.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
var uvAccessorIndex1 = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.uv2.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
|
||||
var colorAccessorIndex = -1;
|
||||
|
||||
var vColorState = MeshExportInfo.DetectVertexColor(mesh, meshMaterials);
|
||||
if (vColorState == MeshExportInfo.VertexColorState.ExistsAndIsUsed // VColor使っている
|
||||
|| vColorState == MeshExportInfo.VertexColorState.ExistsAndMixed // VColorを使っているところと使っていないところが混在(とりあえずExportする)
|
||||
)
|
||||
{
|
||||
// UniUnlit で Multiply 設定になっている
|
||||
colorAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.colors, glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
var weightAccessorIndex = -1;
|
||||
var jointsAccessorIndex = -1;
|
||||
|
||||
if (boneWeights != null && jointIndexMap != null)
|
||||
{
|
||||
weightAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, boneWeights.Select(y => new Vector4(y.weight0, y.weight1, y.weight2, y.weight3)).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
jointsAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, boneWeights.Select(y =>
|
||||
new UShort4(
|
||||
(ushort)jointIndexMap[y.boneIndex0],
|
||||
(ushort)jointIndexMap[y.boneIndex1],
|
||||
(ushort)jointIndexMap[y.boneIndex2],
|
||||
(ushort)jointIndexMap[y.boneIndex3])
|
||||
).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
var attributes = new glTFAttributes
|
||||
{
|
||||
POSITION = positionAccessorIndex,
|
||||
};
|
||||
if (normalAccessorIndex != -1)
|
||||
{
|
||||
attributes.NORMAL = normalAccessorIndex;
|
||||
}
|
||||
#if GLTF_EXPORT_TANGENTS
|
||||
if (tangentAccessorIndex != -1)
|
||||
{
|
||||
attributes.TANGENT = tangentAccessorIndex;
|
||||
}
|
||||
#endif
|
||||
if (uvAccessorIndex0 != -1)
|
||||
{
|
||||
attributes.TEXCOORD_0 = uvAccessorIndex0;
|
||||
}
|
||||
if (uvAccessorIndex1 != -1)
|
||||
{
|
||||
attributes.TEXCOORD_1 = uvAccessorIndex1;
|
||||
}
|
||||
if (colorAccessorIndex != -1)
|
||||
{
|
||||
attributes.COLOR_0 = colorAccessorIndex;
|
||||
}
|
||||
if (weightAccessorIndex != -1)
|
||||
{
|
||||
attributes.WEIGHTS_0 = weightAccessorIndex;
|
||||
}
|
||||
if (jointsAccessorIndex != -1)
|
||||
{
|
||||
attributes.JOINTS_0 = jointsAccessorIndex;
|
||||
}
|
||||
|
||||
var gltfMesh = new glTFMesh(mesh.name);
|
||||
var indices = new List<uint>();
|
||||
for (var j = 0; j < mesh.subMeshCount; ++j)
|
||||
{
|
||||
indices.Clear();
|
||||
|
||||
var triangles = mesh.GetIndices(j);
|
||||
if (triangles.Length == 0)
|
||||
{
|
||||
// https://github.com/vrm-c/UniVRM/issues/664
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var i = 0; i < triangles.Length; i += 3)
|
||||
{
|
||||
var i0 = triangles[i];
|
||||
var i1 = triangles[i + 1];
|
||||
var i2 = triangles[i + 2];
|
||||
|
||||
// flip triangle
|
||||
indices.Add((uint)i2);
|
||||
indices.Add((uint)i1);
|
||||
indices.Add((uint)i0);
|
||||
}
|
||||
|
||||
var indicesAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, indices.ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER);
|
||||
if (indicesAccessorIndex < 0)
|
||||
{
|
||||
// https://github.com/vrm-c/UniVRM/issues/664
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
var primitives = new glTFPrimitives
|
||||
{
|
||||
attributes = attributes,
|
||||
indices = indicesAccessorIndex,
|
||||
mode = 4, // triangles ?
|
||||
};
|
||||
|
||||
if (meshMaterials != null)
|
||||
{
|
||||
primitives.material = exportedMaterials.IndexOf(meshMaterials[j]);
|
||||
}
|
||||
|
||||
gltfMesh.primitives.Add(primitives);
|
||||
|
||||
}
|
||||
return gltfMesh;
|
||||
}
|
||||
|
||||
static glTFMesh ExportPrimitives(glTF gltf, int bufferIndex,
|
||||
/// <summary>
|
||||
/// primitive 間で vertex を共有する形で Export する。
|
||||
///
|
||||
/// UniVRM-0.71.0 までの挙動
|
||||
///
|
||||
/// /// </summary>
|
||||
/// <param name="gltf"></param>
|
||||
/// <param name="bufferIndex"></param>
|
||||
/// <param name="unityMesh"></param>
|
||||
/// <param name="unityMaterials"></param>
|
||||
/// <param name="axisInverter"></param>
|
||||
/// <param name="settings"></param>
|
||||
/// <returns></returns>
|
||||
static glTFMesh ExportSharedVertexBuffer(glTF gltf, int bufferIndex,
|
||||
MeshWithRenderer unityMesh, List<Material> unityMaterials,
|
||||
IAxisInverter axisInverter)
|
||||
IAxisInverter axisInverter, MeshExportSettings settings)
|
||||
{
|
||||
var mesh = unityMesh.Mesh;
|
||||
var materials = unityMesh.Renderer.sharedMaterials;
|
||||
@@ -161,9 +34,13 @@ namespace UniGLTF
|
||||
gltf.accessors[positionAccessorIndex].max = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray();
|
||||
|
||||
var normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.normals.Select(y => axisInverter.InvertVector3(y.normalized)).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
#if GLTF_EXPORT_TANGENTS
|
||||
var tangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.tangents.Select(axisInverter.InvertVector4()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
#endif
|
||||
|
||||
int? tangentAccessorIndex = default;
|
||||
if (settings.ExportTangents)
|
||||
{
|
||||
tangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.tangents.Select(axisInverter.InvertVector4).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
var uvAccessorIndex0 = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.uv.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
var uvAccessorIndex1 = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.uv2.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
|
||||
@@ -196,12 +73,12 @@ namespace UniGLTF
|
||||
{
|
||||
attributes.NORMAL = normalAccessorIndex;
|
||||
}
|
||||
#if GLTF_EXPORT_TANGENTS
|
||||
if (tangentAccessorIndex != -1)
|
||||
|
||||
if (tangentAccessorIndex.HasValue)
|
||||
{
|
||||
attributes.TANGENT = tangentAccessorIndex;
|
||||
attributes.TANGENT = tangentAccessorIndex.Value;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (uvAccessorIndex0 != -1)
|
||||
{
|
||||
attributes.TEXCOORD_0 = uvAccessorIndex0;
|
||||
@@ -411,18 +288,33 @@ namespace UniGLTF
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<(Mesh, glTFMesh, Dictionary<int, int>)> ExportMeshes(glTF gltf, int bufferIndex,
|
||||
List<MeshWithRenderer> unityMeshes, List<Material> unityMaterials,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="mesh"></param>
|
||||
/// <param name="gltf"></param>
|
||||
/// <param name="bufferIndex"></param>
|
||||
/// <param name="unityMesh"></param>
|
||||
/// <param name="unityMaterials"></param>
|
||||
/// <param name="settings"></param>
|
||||
/// <param name="axisInverter"></param>
|
||||
/// <returns></returns>
|
||||
public static (glTFMesh mesh, Dictionary<int, int> blendShapeIndexMap) ExportMesh(glTF gltf, int bufferIndex,
|
||||
MeshWithRenderer unityMesh, List<Material> unityMaterials,
|
||||
MeshExportSettings settings, IAxisInverter axisInverter)
|
||||
{
|
||||
foreach (var unityMesh in unityMeshes)
|
||||
glTFMesh gltfMesh = default;
|
||||
var blendShapeIndexMap = new Dictionary<int, int>();
|
||||
if (settings.DivideVertexBuffer)
|
||||
{
|
||||
var gltfMesh = ExportPrimitives(gltf, bufferIndex,
|
||||
unityMesh, unityMaterials, axisInverter);
|
||||
gltfMesh = MeshExporterDivided.Export(gltf, bufferIndex, unityMesh, unityMaterials, axisInverter, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
gltfMesh = ExportSharedVertexBuffer(gltf, bufferIndex, unityMesh, unityMaterials, axisInverter, settings);
|
||||
|
||||
var targetNames = new List<string>();
|
||||
|
||||
var blendShapeIndexMap = new Dictionary<int, int>();
|
||||
int exportBlendShapes = 0;
|
||||
for (int j = 0; j < unityMesh.Mesh.blendShapeCount; ++j)
|
||||
{
|
||||
@@ -450,66 +342,7 @@ namespace UniGLTF
|
||||
}
|
||||
|
||||
gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames);
|
||||
|
||||
yield return (unityMesh.Mesh, gltfMesh, blendShapeIndexMap);
|
||||
}
|
||||
}
|
||||
|
||||
public static (glTFMesh gltfMesh, Dictionary<int, int> blendShapeIndexMap) ExportMesh(glTF gltf, int bufferIndex, Mesh mesh, Renderer renderer, List<Material> exportedMaterials, MeshExportSettings meshExportSettings, IAxisInverter axisInverter)
|
||||
{
|
||||
var meshMaterials = default(Material[]);
|
||||
if (renderer != null)
|
||||
{
|
||||
meshMaterials = renderer.sharedMaterials;
|
||||
}
|
||||
|
||||
var boneWeights = default(BoneWeight[]);
|
||||
var jointIndexMap = default(int[]);
|
||||
if (renderer is SkinnedMeshRenderer skin)
|
||||
{
|
||||
var bones = skin.bones;
|
||||
var uniqueBones = bones.Distinct().ToArray();
|
||||
jointIndexMap = new int[bones.Length];
|
||||
for (var i = 0; i < bones.Length; i++)
|
||||
{
|
||||
jointIndexMap[i] = Array.IndexOf(uniqueBones, bones[i]);
|
||||
}
|
||||
|
||||
boneWeights = mesh.boneWeights;
|
||||
}
|
||||
|
||||
var gltfMesh = ExportPrimitives(gltf, bufferIndex, mesh, meshMaterials, boneWeights, jointIndexMap, exportedMaterials);
|
||||
|
||||
var targetNames = new List<string>();
|
||||
|
||||
var blendShapeIndexMap = new Dictionary<int, int>();
|
||||
int exportBlendShapes = 0;
|
||||
for (int j = 0; j < mesh.blendShapeCount; ++j)
|
||||
{
|
||||
var morphTarget = ExportMorphTarget(gltf, bufferIndex,
|
||||
mesh, j,
|
||||
meshExportSettings.UseSparseAccessorForMorphTarget,
|
||||
meshExportSettings.ExportOnlyBlendShapePosition, axisInverter);
|
||||
if (morphTarget.POSITION < 0 && morphTarget.NORMAL < 0 && morphTarget.TANGENT < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// maybe skip
|
||||
var blendShapeName = mesh.GetBlendShapeName(j);
|
||||
blendShapeIndexMap.Add(j, exportBlendShapes++);
|
||||
targetNames.Add(blendShapeName);
|
||||
|
||||
//
|
||||
// all primitive has same blendShape
|
||||
//
|
||||
for (int k = 0; k < gltfMesh.primitives.Count; ++k)
|
||||
{
|
||||
gltfMesh.primitives[k].targets.Add(morphTarget);
|
||||
}
|
||||
}
|
||||
|
||||
gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames);
|
||||
|
||||
return (gltfMesh, blendShapeIndexMap);
|
||||
}
|
||||
|
||||
217
Assets/UniGLTF/Runtime/UniGLTF/IO/MeshExporterDivided.cs
Normal file
217
Assets/UniGLTF/Runtime/UniGLTF/IO/MeshExporterDivided.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class MeshExporterDivided
|
||||
{
|
||||
class BlendShapeBuffer
|
||||
{
|
||||
readonly List<Vector3> m_positions;
|
||||
readonly List<Vector3> m_normals;
|
||||
|
||||
public BlendShapeBuffer(int reserve)
|
||||
{
|
||||
m_positions = new List<Vector3>(reserve);
|
||||
m_normals = new List<Vector3>(reserve);
|
||||
}
|
||||
|
||||
public void Push(Vector3 position, Vector3 normal)
|
||||
{
|
||||
m_positions.Add(position);
|
||||
m_normals.Add(normal);
|
||||
}
|
||||
|
||||
public gltfMorphTarget ToGltf(glTF gltf, int bufferIndex, bool useNormal)
|
||||
{
|
||||
var positionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_positions.ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
var normalAccessorIndex = -1;
|
||||
if (useNormal)
|
||||
{
|
||||
normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_normals.ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
return new gltfMorphTarget
|
||||
{
|
||||
POSITION = positionAccessorIndex,
|
||||
NORMAL = normalAccessorIndex,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class VertexBuffer
|
||||
{
|
||||
readonly List<Vector3> m_positions;
|
||||
readonly List<Vector3> m_normals;
|
||||
readonly List<Vector2> m_uv;
|
||||
|
||||
readonly Func<int, int> m_getJointIndex;
|
||||
readonly List<UShort4> m_joints;
|
||||
readonly List<Vector4> m_weights;
|
||||
|
||||
public VertexBuffer(int reserve, Func<int, int> getJointIndex)
|
||||
{
|
||||
m_positions = new List<Vector3>(reserve);
|
||||
m_normals = new List<Vector3>(reserve);
|
||||
m_uv = new List<Vector2>();
|
||||
|
||||
m_getJointIndex = getJointIndex;
|
||||
if (m_getJointIndex != null)
|
||||
{
|
||||
m_joints = new List<UShort4>(reserve);
|
||||
m_weights = new List<Vector4>(reserve);
|
||||
}
|
||||
}
|
||||
|
||||
public void Push(Vector3 position, Vector3 normal, Vector2 uv)
|
||||
{
|
||||
m_positions.Add(position);
|
||||
m_normals.Add(normal);
|
||||
m_uv.Add(uv);
|
||||
}
|
||||
|
||||
public void Push(BoneWeight boneWeight)
|
||||
{
|
||||
m_joints.Add(new UShort4((ushort)boneWeight.boneIndex0, (ushort)boneWeight.boneIndex1, (ushort)boneWeight.boneIndex2, (ushort)boneWeight.boneIndex3));
|
||||
m_weights.Add(new Vector4(boneWeight.weight0, boneWeight.weight1, boneWeight.weight2, boneWeight.weight3));
|
||||
}
|
||||
|
||||
public glTFPrimitives ToGltf(glTF gltf, int bufferIndex, int materialIndex, IReadOnlyList<uint> indices)
|
||||
{
|
||||
var indicesAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, indices.ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER);
|
||||
var positionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_positions.ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
var normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_normals.ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
var uvAccessorIndex0 = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_uv.ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
|
||||
int? jointsAccessorIndex = default;
|
||||
if (m_joints != null)
|
||||
{
|
||||
jointsAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_joints.ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
int? weightAccessorIndex = default;
|
||||
if (m_weights != null)
|
||||
{
|
||||
weightAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_weights.ToArray(), glBufferTarget.ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
var primitive = new glTFPrimitives
|
||||
{
|
||||
indices = indicesAccessorIndex,
|
||||
attributes = new glTFAttributes
|
||||
{
|
||||
POSITION = positionAccessorIndex,
|
||||
NORMAL = normalAccessorIndex,
|
||||
TEXCOORD_0 = uvAccessorIndex0,
|
||||
JOINTS_0 = jointsAccessorIndex.GetValueOrDefault(-1),
|
||||
WEIGHTS_0 = weightAccessorIndex.GetValueOrDefault(-1),
|
||||
},
|
||||
material = materialIndex,
|
||||
mode = 4,
|
||||
};
|
||||
return primitive;
|
||||
}
|
||||
}
|
||||
|
||||
public static glTFMesh Export(glTF gltf, int bufferIndex,
|
||||
MeshWithRenderer unityMesh, List<Material> unityMaterials,
|
||||
IAxisInverter axisInverter, MeshExportSettings settings)
|
||||
{
|
||||
var mesh = unityMesh.Mesh;
|
||||
var gltfMesh = new glTFMesh(mesh.name);
|
||||
|
||||
if (settings.ExportTangents)
|
||||
{
|
||||
// support しない
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
var positions = mesh.vertices;
|
||||
var normals = mesh.normals;
|
||||
var uv = mesh.uv;
|
||||
var boneWeights = mesh.boneWeights;
|
||||
|
||||
Func<int, int> getJointIndex = null;
|
||||
if (boneWeights != null && boneWeights.Length == positions.Length)
|
||||
{
|
||||
getJointIndex = unityMesh.GetJointIndex;
|
||||
}
|
||||
|
||||
Vector3[] blendShapePositions = new Vector3[mesh.vertexCount];
|
||||
Vector3[] blendShapeNormals = new Vector3[mesh.vertexCount];
|
||||
|
||||
var usedIndices = new List<int>();
|
||||
for (int i = 0; i < mesh.subMeshCount; ++i)
|
||||
{
|
||||
var indices = mesh.GetIndices(i);
|
||||
|
||||
// mesh
|
||||
// index の順に attributes を蓄える
|
||||
var buffer = new VertexBuffer(indices.Length, getJointIndex);
|
||||
// foreach (var k in indices)
|
||||
// {
|
||||
// buffer.Push(axisInverter.InvertVector3(positions[k]), axisInverter.InvertVector3(normals[k]), uv[k].ReverseUV());
|
||||
// if (getJointIndex != null)
|
||||
// {
|
||||
// buffer.Push(boneWeights[k]);
|
||||
// }
|
||||
// }
|
||||
// indices から参照される頂点だけを蓄える
|
||||
usedIndices.Clear();
|
||||
for (int k = 0; k < positions.Length; ++k)
|
||||
{
|
||||
if (indices.Contains(k))
|
||||
{
|
||||
usedIndices.Add(k);
|
||||
buffer.Push(axisInverter.InvertVector3(positions[k]), axisInverter.InvertVector3(normals[k]), uv[k].ReverseUV());
|
||||
if (getJointIndex != null)
|
||||
{
|
||||
buffer.Push(boneWeights[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var material = unityMesh.Renderer.sharedMaterials[i];
|
||||
var materialIndex = -1;
|
||||
if (material != null)
|
||||
{
|
||||
materialIndex = unityMaterials.IndexOf(material);
|
||||
}
|
||||
var indexMap = usedIndices.Select((used, index) => (used, index)).ToDictionary(x => x.used, x => (uint)x.index);
|
||||
|
||||
var flipped = new List<uint>();
|
||||
for (int j = 0; j < indices.Length; j += 3)
|
||||
{
|
||||
flipped.Add((uint)indexMap[indices[j + 2]]);
|
||||
flipped.Add((uint)indexMap[indices[j + 1]]);
|
||||
flipped.Add((uint)indexMap[indices[j]]);
|
||||
}
|
||||
var gltfPrimitive = buffer.ToGltf(gltf, bufferIndex, materialIndex, flipped);
|
||||
|
||||
// blendShape
|
||||
for (int j = 0; j < mesh.blendShapeCount; ++j)
|
||||
{
|
||||
var blendShape = new BlendShapeBuffer(indices.Length);
|
||||
|
||||
// index の順に attributes を蓄える
|
||||
mesh.GetBlendShapeFrameVertices(j, 0, blendShapePositions, blendShapeNormals, null);
|
||||
foreach (var k in usedIndices)
|
||||
{
|
||||
blendShape.Push(
|
||||
axisInverter.InvertVector3(blendShapePositions[k]),
|
||||
axisInverter.InvertVector3(blendShapeNormals[k]));
|
||||
}
|
||||
|
||||
gltfPrimitive.targets.Add(blendShape.ToGltf(gltf, bufferIndex, !settings.ExportOnlyBlendShapePosition));
|
||||
}
|
||||
|
||||
gltfMesh.primitives.Add(gltfPrimitive);
|
||||
}
|
||||
|
||||
var targetNames = Enumerable.Range(0, mesh.blendShapeCount).Select(x => mesh.GetBlendShapeName(x)).ToArray();
|
||||
gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames);
|
||||
|
||||
return gltfMesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92bc82be5ead84a429346379590f6d7d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -37,6 +37,12 @@ namespace UniGLTF
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// glTF は skinning の boneList の重複を許可しない
|
||||
/// (unity は ok)
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public int GetJointIndex(int index)
|
||||
{
|
||||
if (index < 0)
|
||||
|
||||
@@ -17,11 +17,7 @@ namespace UniGLTF
|
||||
protected set;
|
||||
}
|
||||
|
||||
public List<Mesh> Meshes
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public List<Mesh> Meshes { get; private set; } = new List<Mesh>();
|
||||
|
||||
/// <summary>
|
||||
/// Mesh毎に、元のBlendShapeIndex => ExportされたBlendShapeIndex の対応を記録する
|
||||
@@ -195,17 +191,17 @@ namespace UniGLTF
|
||||
var unityMeshes = MeshWithRenderer.FromNodes(Nodes).Where(x => x.Mesh.vertices.Any()).ToList();
|
||||
|
||||
MeshBlendShapeIndexMap = new Dictionary<Mesh, Dictionary<int, int>>();
|
||||
foreach (var (mesh, gltfMesh, blendShapeIndexMap) in MeshExporter.ExportMeshes(
|
||||
glTF, bufferIndex, unityMeshes, Materials, meshExportSettings, m_axisInverter))
|
||||
foreach (var unityMesh in unityMeshes)
|
||||
{
|
||||
var (gltfMesh, blendShapeIndexMap) = MeshExporter.ExportMesh(glTF, bufferIndex, unityMesh, Materials, meshExportSettings, m_axisInverter);
|
||||
glTF.meshes.Add(gltfMesh);
|
||||
if (!MeshBlendShapeIndexMap.ContainsKey(mesh))
|
||||
Meshes.Add(unityMesh.Mesh);
|
||||
if (!MeshBlendShapeIndexMap.ContainsKey(unityMesh.Mesh))
|
||||
{
|
||||
// 同じmeshが複数回現れた
|
||||
MeshBlendShapeIndexMap.Add(mesh, blendShapeIndexMap);
|
||||
MeshBlendShapeIndexMap.Add(unityMesh.Mesh, blendShapeIndexMap);
|
||||
}
|
||||
}
|
||||
Meshes = unityMeshes.Select(x => x.Mesh).ToList();
|
||||
#endregion
|
||||
|
||||
#region Nodes and Skins
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
@@ -11,8 +12,8 @@ namespace UniGLTF
|
||||
public void AccessorTest()
|
||||
{
|
||||
byte[] bytes = default;
|
||||
using(var ms = new MemoryStream())
|
||||
using(var w = new BinaryWriter(ms))
|
||||
using (var ms = new MemoryStream())
|
||||
using (var w = new BinaryWriter(ms))
|
||||
{
|
||||
w.Write(1.0f);
|
||||
w.Write(2.0f);
|
||||
@@ -28,7 +29,7 @@ namespace UniGLTF
|
||||
|
||||
var gltf = new glTF
|
||||
{
|
||||
buffers=new List<glTFBuffer>
|
||||
buffers = new List<glTFBuffer>
|
||||
{
|
||||
new glTFBuffer
|
||||
{
|
||||
@@ -39,7 +40,7 @@ namespace UniGLTF
|
||||
new glTFBufferView{
|
||||
buffer=0,
|
||||
byteLength=32,
|
||||
byteOffset=0,
|
||||
byteOffset=0,
|
||||
}
|
||||
},
|
||||
accessors = new List<glTFAccessor>
|
||||
@@ -59,5 +60,153 @@ namespace UniGLTF
|
||||
Assert.AreEqual((1.0f, 2.0f, 3.0f, 4.0f), getter(0));
|
||||
Assert.AreEqual((5.0f, 6.0f, 7.0f, 8.0f), getter(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 0 1 2
|
||||
/// +-+-+
|
||||
/// |A|B|
|
||||
/// +-+-+
|
||||
/// 5 4 3
|
||||
///
|
||||
/// 6 vertices
|
||||
/// 4 triangles
|
||||
/// 2 materials
|
||||
///
|
||||
/// </summary>
|
||||
static (GameObject, Mesh) CreateMesh(Material[] materials)
|
||||
{
|
||||
var unityMesh = new Mesh();
|
||||
unityMesh.vertices = new Vector3[]
|
||||
{
|
||||
new Vector3(0, 1, 0),
|
||||
new Vector3(1, 1, 0),
|
||||
new Vector3(2, 1, 0),
|
||||
new Vector3(2, 0, 0),
|
||||
new Vector3(1, 0, 0),
|
||||
new Vector3(0, 0, 0),
|
||||
};
|
||||
unityMesh.uv = new Vector2[]
|
||||
{
|
||||
new Vector2(0, 0),
|
||||
new Vector2(1, 0),
|
||||
new Vector2(2, 0),
|
||||
new Vector2(2, 1),
|
||||
new Vector2(1, 1),
|
||||
new Vector2(0, 1),
|
||||
};
|
||||
|
||||
unityMesh.subMeshCount = 2;
|
||||
unityMesh.SetTriangles(new int[]{
|
||||
0, 1, 5,
|
||||
5, 1, 4,
|
||||
}, 0);
|
||||
unityMesh.SetTriangles(new int[]{
|
||||
1, 2, 4,
|
||||
4, 2, 3,
|
||||
}, 1);
|
||||
|
||||
unityMesh.RecalculateNormals();
|
||||
unityMesh.RecalculateTangents();
|
||||
|
||||
var go = new GameObject();
|
||||
go.AddComponent<MeshRenderer>().sharedMaterials = materials;
|
||||
go.AddComponent<MeshFilter>().sharedMesh = unityMesh;
|
||||
|
||||
return (go, unityMesh);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SharedVertexBufferTest()
|
||||
{
|
||||
var glTF = new glTF();
|
||||
var bytesBuffer = new ArrayByteBuffer(new byte[50 * 1024 * 1024]);
|
||||
var bufferIndex = glTF.AddBuffer(bytesBuffer);
|
||||
|
||||
var Materials = new List<Material>{
|
||||
new Material(Shader.Find("Standard")), // A
|
||||
new Material(Shader.Find("Standard")), // B
|
||||
};
|
||||
|
||||
var (go, unityMesh) = CreateMesh(Materials.ToArray());
|
||||
var meshExportSettings = new MeshExportSettings
|
||||
{
|
||||
DivideVertexBuffer = false
|
||||
};
|
||||
var axisInverter = Axises.X.Create();
|
||||
|
||||
var (gltfMesh, blendShapeIndexMap) = MeshExporter.ExportMesh(glTF, bufferIndex, new MeshWithRenderer(go.transform), Materials, meshExportSettings, axisInverter);
|
||||
|
||||
{
|
||||
var indices = glTF.GetIndices(gltfMesh.primitives[0].indices);
|
||||
Assert.AreEqual(0, indices[0]);
|
||||
Assert.AreEqual(1, indices[1]);
|
||||
Assert.AreEqual(5, indices[2]);
|
||||
Assert.AreEqual(5, indices[3]);
|
||||
Assert.AreEqual(1, indices[4]);
|
||||
Assert.AreEqual(4, indices[5]);
|
||||
}
|
||||
{
|
||||
var indices = glTF.GetIndices(gltfMesh.primitives[1].indices);
|
||||
Assert.AreEqual(1, indices[0]);
|
||||
Assert.AreEqual(2, indices[1]);
|
||||
Assert.AreEqual(4, indices[2]);
|
||||
Assert.AreEqual(4, indices[3]);
|
||||
Assert.AreEqual(2, indices[4]);
|
||||
Assert.AreEqual(3, indices[5]);
|
||||
}
|
||||
|
||||
var positions = glTF.GetArrayFromAccessor<Vector3>(gltfMesh.primitives[0].attributes.POSITION);
|
||||
Assert.AreEqual(6, positions.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DividedVertexBufferTest()
|
||||
{
|
||||
var glTF = new glTF();
|
||||
var bytesBuffer = new ArrayByteBuffer(new byte[50 * 1024 * 1024]);
|
||||
var bufferIndex = glTF.AddBuffer(bytesBuffer);
|
||||
|
||||
var Materials = new List<Material>{
|
||||
new Material(Shader.Find("Standard")), // A
|
||||
new Material(Shader.Find("Standard")), // B
|
||||
};
|
||||
|
||||
var (go, unityMesh) = CreateMesh(Materials.ToArray());
|
||||
var meshExportSettings = new MeshExportSettings
|
||||
{
|
||||
DivideVertexBuffer = true
|
||||
};
|
||||
var axisInverter = Axises.X.Create();
|
||||
|
||||
var (gltfMesh, blendShapeIndexMap) = MeshExporter.ExportMesh(glTF, bufferIndex, new MeshWithRenderer(go.transform), Materials, meshExportSettings, axisInverter);
|
||||
|
||||
{
|
||||
var indices = glTF.GetIndices(gltfMesh.primitives[0].indices);
|
||||
Assert.AreEqual(0, indices[0]);
|
||||
Assert.AreEqual(1, indices[1]);
|
||||
Assert.AreEqual(3, indices[2]);
|
||||
Assert.AreEqual(3, indices[3]);
|
||||
Assert.AreEqual(1, indices[4]);
|
||||
Assert.AreEqual(2, indices[5]);
|
||||
}
|
||||
{
|
||||
var positions = glTF.GetArrayFromAccessor<Vector3>(gltfMesh.primitives[0].attributes.POSITION);
|
||||
Assert.AreEqual(4, positions.Length);
|
||||
}
|
||||
|
||||
{
|
||||
var indices = glTF.GetIndices(gltfMesh.primitives[1].indices);
|
||||
Assert.AreEqual(0, indices[0]);
|
||||
Assert.AreEqual(1, indices[1]);
|
||||
Assert.AreEqual(3, indices[2]);
|
||||
Assert.AreEqual(3, indices[3]);
|
||||
Assert.AreEqual(1, indices[4]);
|
||||
Assert.AreEqual(2, indices[5]);
|
||||
}
|
||||
{
|
||||
var positions = glTF.GetArrayFromAccessor<Vector3>(gltfMesh.primitives[1].attributes.POSITION);
|
||||
Assert.AreEqual(4, positions.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniGLTF;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRM
|
||||
@@ -7,6 +7,7 @@ namespace VRM
|
||||
[Serializable]
|
||||
public class VRMExportSettings : ScriptableObject
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// エクスポート時に強制的にT-Pose化する
|
||||
/// </summary>
|
||||
@@ -43,10 +44,17 @@ namespace VRM
|
||||
[Tooltip("Remove blendShapeClip that preset is Unknown")]
|
||||
public bool ReduceBlendshapeClip = false;
|
||||
|
||||
public UniGLTF.MeshExportSettings MeshExportSettings => new UniGLTF.MeshExportSettings
|
||||
/// <summary>
|
||||
/// Export時に頂点バッファをsubmeshで分割する。GLTF互換性
|
||||
/// </summary>
|
||||
[Tooltip("Divide vertex buffer. For more gltf compatibility")]
|
||||
public bool DivideVertexBuffer = false;
|
||||
|
||||
public MeshExportSettings MeshExportSettings => new MeshExportSettings
|
||||
{
|
||||
UseSparseAccessorForMorphTarget = UseSparseAccessor,
|
||||
ExportOnlyBlendShapePosition = OnlyBlendshapePosition,
|
||||
DivideVertexBuffer = DivideVertexBuffer,
|
||||
};
|
||||
|
||||
public GameObject Root { get; set; }
|
||||
|
||||
@@ -36,47 +36,12 @@ namespace VRM
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// エクスポート時にヒエラルキーの正規化を実施する
|
||||
/// </summary>
|
||||
[Tooltip("Require only first time")]
|
||||
public bool PoseFreeze = true;
|
||||
|
||||
/// <summary>
|
||||
/// エクスポート時に新しいJsonSerializerを使う
|
||||
/// </summary>
|
||||
[Tooltip("Use new JSON serializer")]
|
||||
public bool UseExperimentalExporter = false;
|
||||
|
||||
/// <summary>
|
||||
/// BlendShapeのシリアライズにSparseAccessorを使う
|
||||
/// </summary>
|
||||
[Tooltip("Use sparse accessor for blendshape. This may reduce vrm size")]
|
||||
public bool UseSparseAccessor = false;
|
||||
|
||||
/// <summary>
|
||||
/// BlendShapeのPositionのみをエクスポートする
|
||||
/// </summary>
|
||||
[Tooltip("UniVRM-0.54 or later can load it. Otherwise fail to load")]
|
||||
public bool OnlyBlendshapePosition = false;
|
||||
|
||||
/// <summary>
|
||||
/// エクスポート時にBlendShapeClipから参照されないBlendShapeを削除する
|
||||
/// </summary>
|
||||
[Tooltip("Remove blendshape that is not used from BlendShapeClip")]
|
||||
public bool ReduceBlendshape = false;
|
||||
|
||||
/// <summary>
|
||||
/// skip if BlendShapeClip.Preset == Unknown
|
||||
/// </summary>
|
||||
[Tooltip("Remove blendShapeClip that preset is Unknown")]
|
||||
public bool ReduceBlendshapeClip = false;
|
||||
|
||||
CheckBoxProp m_poseFreeze;
|
||||
CheckBoxProp m_useSparseAccessor;
|
||||
CheckBoxProp m_onlyBlendShapePosition;
|
||||
CheckBoxProp m_reduceBlendShape;
|
||||
CheckBoxProp m_reduceBlendShapeClip;
|
||||
CheckBoxProp m_divideVertexBuffer;
|
||||
|
||||
static string Msg(Options key)
|
||||
{
|
||||
@@ -128,15 +93,20 @@ namespace VRM
|
||||
[LangMsg(Languages.ja, "T-Pose にする")]
|
||||
[LangMsg(Languages.en, "Make T-Pose")]
|
||||
DO_TPOSE,
|
||||
|
||||
[LangMsg(Languages.ja, "頂点バッファをsubmeshで分割する。GLTF互換性のため。UniVRM-0.72 からロードできる。")]
|
||||
[LangMsg(Languages.en, "Divide vertex buffer by submesh。For more gltf compatibility。UniVRM-0.72 or later can load.")]
|
||||
DIVIDE_VERTEX_BUFFER,
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_poseFreeze = new CheckBoxProp(serializedObject.FindProperty(nameof(PoseFreeze)), Options.NORMALIZE);
|
||||
m_useSparseAccessor = new CheckBoxProp(serializedObject.FindProperty(nameof(UseSparseAccessor)), Options.BLENDSHAPE_USE_SPARSE);
|
||||
m_onlyBlendShapePosition = new CheckBoxProp(serializedObject.FindProperty(nameof(OnlyBlendshapePosition)), Options.BLENDSHAPE_EXCLUDE_NORMAL_AND_TANGENT);
|
||||
m_reduceBlendShape = new CheckBoxProp(serializedObject.FindProperty(nameof(ReduceBlendshape)), Options.BLENDSHAPE_ONLY_CLIP_USE);
|
||||
m_reduceBlendShapeClip = new CheckBoxProp(serializedObject.FindProperty(nameof(ReduceBlendshapeClip)), Options.BLENDSHAPE_EXCLUDE_UNKNOWN);
|
||||
m_poseFreeze = new CheckBoxProp(serializedObject.FindProperty(nameof(VRMExportSettings.PoseFreeze)), Options.NORMALIZE);
|
||||
m_useSparseAccessor = new CheckBoxProp(serializedObject.FindProperty(nameof(VRMExportSettings.UseSparseAccessor)), Options.BLENDSHAPE_USE_SPARSE);
|
||||
m_onlyBlendShapePosition = new CheckBoxProp(serializedObject.FindProperty(nameof(VRMExportSettings.OnlyBlendshapePosition)), Options.BLENDSHAPE_EXCLUDE_NORMAL_AND_TANGENT);
|
||||
m_reduceBlendShape = new CheckBoxProp(serializedObject.FindProperty(nameof(VRMExportSettings.ReduceBlendshape)), Options.BLENDSHAPE_ONLY_CLIP_USE);
|
||||
m_reduceBlendShapeClip = new CheckBoxProp(serializedObject.FindProperty(nameof(VRMExportSettings.ReduceBlendshapeClip)), Options.BLENDSHAPE_EXCLUDE_UNKNOWN);
|
||||
m_divideVertexBuffer = new CheckBoxProp(serializedObject.FindProperty(nameof(VRMExportSettings.DivideVertexBuffer)), Options.DIVIDE_VERTEX_BUFFER);
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
@@ -173,6 +143,8 @@ namespace VRM
|
||||
m_onlyBlendShapePosition.Draw();
|
||||
m_reduceBlendShape.Draw();
|
||||
m_reduceBlendShapeClip.Draw();
|
||||
m_divideVertexBuffer.Draw();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user