mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-04 20:27:01 -05:00
266 lines
11 KiB
C#
266 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
|
|
namespace UniGLTF
|
|
{
|
|
public static class MeshExporter_SharedVertexBuffer
|
|
{
|
|
/// <summary>
|
|
/// primitive 間で vertex を共有する形で Export する。
|
|
/// UniVRM-0.71.0 以降は、MeshExporterDivided.Export もある。
|
|
///
|
|
/// * GLB/GLTF は shared(default) と divided を選択可能
|
|
/// * VRM0 は shared 仕様
|
|
/// * VRM1 は divided 仕様
|
|
///
|
|
/// /// </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>
|
|
public static (glTFMesh, Dictionary<int, int> blendShapeIndexMap) Export(ExportingGltfData data,
|
|
MeshExportInfo unityMesh, List<Material> unityMaterials,
|
|
IAxisInverter axisInverter, GltfExportSettings settings)
|
|
{
|
|
var mesh = unityMesh.Mesh;
|
|
var materials = unityMesh.Materials;
|
|
var positions = mesh.vertices.Select(axisInverter.InvertVector3).ToArray();
|
|
var positionAccessorIndex = data.ExtendBufferAndGetAccessorIndex(positions, glBufferTarget.ARRAY_BUFFER);
|
|
AccessorsBounds.UpdatePositionAccessorsBounds(data.Gltf.accessors[positionAccessorIndex], positions);
|
|
|
|
var normalAccessorIndex = data.ExtendBufferAndGetAccessorIndex(mesh.normals.Select(y => axisInverter.InvertVector3(y.normalized)).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
|
|
|
int? tangentAccessorIndex = default;
|
|
if (settings.ExportTangents)
|
|
{
|
|
tangentAccessorIndex = data.ExtendBufferAndGetAccessorIndex(mesh.tangents.Select(axisInverter.InvertVector4).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
|
}
|
|
|
|
var uvAccessorIndex0 = data.ExtendBufferAndGetAccessorIndex(mesh.uv.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
|
|
|
var uvAccessorIndex1 = -1;
|
|
if (settings.ExportUvSecondary)
|
|
{
|
|
uvAccessorIndex1 = data.ExtendBufferAndGetAccessorIndex(mesh.uv2.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
|
}
|
|
|
|
var colorAccessorIndex = -1;
|
|
var vColorState = VertexColorUtility.DetectVertexColor(mesh, materials);
|
|
if ((settings.ExportVertexColor && mesh.colors != null && mesh.colors.Length == mesh.vertexCount) // vertex color を残す設定
|
|
|| vColorState == VertexColorState.ExistsAndIsUsed // VColor使っている
|
|
|| vColorState == VertexColorState.ExistsAndMixed // VColorを使っているところと使っていないところが混在(とりあえずExportする)
|
|
)
|
|
{
|
|
// UniUnlit で Multiply 設定になっている
|
|
colorAccessorIndex = data.ExtendBufferAndGetAccessorIndex(mesh.colors, glBufferTarget.ARRAY_BUFFER);
|
|
}
|
|
|
|
var boneWeights = mesh.boneWeights;
|
|
var weightAccessorIndex = -1;
|
|
var jointsAccessorIndex = -1;
|
|
if (boneWeights.All(x => x.weight0 == 0 && x.weight1 == 0 && x.weight2 == 0 && x.weight3 == 0))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
weightAccessorIndex = data.ExtendBufferAndGetAccessorIndex(boneWeights.Select(y => new Vector4(y.weight0, y.weight1, y.weight2, y.weight3)).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
|
jointsAccessorIndex = data.ExtendBufferAndGetAccessorIndex(boneWeights.Select(y =>
|
|
new UShort4(
|
|
(ushort)unityMesh.GetJointIndex(y.boneIndex0),
|
|
(ushort)unityMesh.GetJointIndex(y.boneIndex1),
|
|
(ushort)unityMesh.GetJointIndex(y.boneIndex2),
|
|
(ushort)unityMesh.GetJointIndex(y.boneIndex3))
|
|
).ToArray(), glBufferTarget.ARRAY_BUFFER);
|
|
}
|
|
|
|
var attributes = new glTFAttributes
|
|
{
|
|
POSITION = positionAccessorIndex,
|
|
};
|
|
if (normalAccessorIndex != -1)
|
|
{
|
|
attributes.NORMAL = normalAccessorIndex;
|
|
}
|
|
|
|
if (tangentAccessorIndex.HasValue)
|
|
{
|
|
attributes.TANGENT = tangentAccessorIndex.Value;
|
|
}
|
|
|
|
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 (int 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 (int 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 = data.ExtendBufferAndGetAccessorIndex(indices.ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER);
|
|
if (indicesAccessorIndex < 0)
|
|
{
|
|
// https://github.com/vrm-c/UniVRM/issues/664
|
|
throw new Exception();
|
|
}
|
|
|
|
if (j >= materials.Length)
|
|
{
|
|
Debug.LogWarningFormat("{0}.materials is not enough", unityMesh.Mesh.name);
|
|
break;
|
|
}
|
|
|
|
gltfMesh.primitives.Add(new glTFPrimitives
|
|
{
|
|
attributes = attributes,
|
|
indices = indicesAccessorIndex,
|
|
mode = 4, // triangles ?
|
|
material = unityMaterials.IndexOf(materials[j])
|
|
});
|
|
}
|
|
|
|
var blendShapeIndexMap = new Dictionary<int, int>();
|
|
{
|
|
var targetNames = new List<string>();
|
|
|
|
int exportBlendShapes = 0;
|
|
for (int j = 0; j < unityMesh.Mesh.blendShapeCount; ++j)
|
|
{
|
|
var morphTarget = ExportMorphTarget(data,
|
|
unityMesh.Mesh, j,
|
|
settings.UseSparseAccessorForMorphTarget,
|
|
settings.ExportOnlyBlendShapePosition, axisInverter);
|
|
if (morphTarget.POSITION < 0)
|
|
{
|
|
// Skip empty blendShape.
|
|
// Shift blendShape's index.
|
|
continue;
|
|
}
|
|
|
|
var blendShapeName = unityMesh.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, BlendShapeTargetNameLocationFlags.Both);
|
|
}
|
|
|
|
return (gltfMesh, blendShapeIndexMap);
|
|
}
|
|
|
|
static bool UseSparse(
|
|
bool usePosition, Vector3 position,
|
|
bool useNormal, Vector3 normal,
|
|
bool useTangent, Vector3 tangent
|
|
)
|
|
{
|
|
var useSparse =
|
|
(usePosition && position != Vector3.zero)
|
|
|| (useNormal && normal != Vector3.zero)
|
|
|| (useTangent && tangent != Vector3.zero)
|
|
;
|
|
return useSparse;
|
|
}
|
|
|
|
static gltfMorphTarget ExportMorphTarget(ExportingGltfData data,
|
|
Mesh mesh, int blendShapeIndex,
|
|
bool useSparseAccessorForMorphTarget,
|
|
bool exportOnlyBlendShapePosition,
|
|
IAxisInverter axisInverter)
|
|
{
|
|
var blendShapeVertices = mesh.vertices;
|
|
var usePosition = blendShapeVertices != null && blendShapeVertices.Length > 0;
|
|
|
|
var blendShapeNormals = mesh.normals;
|
|
var useNormal = usePosition && blendShapeNormals != null && blendShapeNormals.Length == blendShapeVertices.Length;
|
|
// var useNormal = usePosition && blendShapeNormals != null && blendShapeNormals.Length == blendShapeVertices.Length && !exportOnlyBlendShapePosition;
|
|
|
|
// var blendShapeTangents = mesh.tangents.Select(y => (Vector3)y).ToArray();
|
|
// //var useTangent = usePosition && blendShapeTangents != null && blendShapeTangents.Length == blendShapeVertices.Length;
|
|
// var useTangent = false;
|
|
|
|
var frameCount = mesh.GetBlendShapeFrameCount(blendShapeIndex);
|
|
mesh.GetBlendShapeFrameVertices(blendShapeIndex, frameCount - 1, blendShapeVertices, blendShapeNormals, null);
|
|
|
|
//
|
|
// invert axis
|
|
//
|
|
for (int i = 0; i < blendShapeVertices.Length; ++i)
|
|
{
|
|
blendShapeVertices[i] = axisInverter.InvertVector3(blendShapeVertices[i]);
|
|
}
|
|
for (int i = 0; i < blendShapeNormals.Length; ++i)
|
|
{
|
|
blendShapeNormals[i] = axisInverter.InvertVector3(blendShapeNormals[i]);
|
|
}
|
|
|
|
var positions = mesh.vertices;
|
|
for (int i = 0; i < positions.Length; ++i)
|
|
{
|
|
positions[i] = axisInverter.InvertVector3(positions[i]);
|
|
}
|
|
|
|
var normals = mesh.normals;
|
|
for (int i = 0; i < normals.Length; ++i)
|
|
{
|
|
normals[i] = axisInverter.InvertVector3(normals[i]);
|
|
}
|
|
|
|
return BlendShapeExporter.Export(data,
|
|
blendShapeVertices,
|
|
exportOnlyBlendShapePosition && useNormal ? null : blendShapeNormals,
|
|
useSparseAccessorForMorphTarget);
|
|
}
|
|
}
|
|
}
|