Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/mesh export divided #912

Merged
merged 3 commits into from
Apr 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 54 additions & 25 deletions Assets/UniGLTF/Runtime/UniGLTF/IO/MeshExporterDivided.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace UniGLTF
{
public static class MeshExporterDivided
{
class BlendShapeBuffer
public class BlendShapeBuffer
{
readonly List<Vector3> m_positions;
readonly List<Vector3> m_normals;
Expand Down Expand Up @@ -40,8 +40,32 @@ public gltfMorphTarget ToGltf(glTF gltf, int bufferIndex, bool useNormal)
}
}

class VertexBuffer
public class VertexBuffer
{
/// <summary>
/// SubMeshで分割するので index が変わる。対応表
/// </summary>
/// <typeparam name="int"></typeparam>
/// <typeparam name="int"></typeparam>
/// <returns></returns>
readonly Dictionary<int, int> m_vertexIndexMap = new Dictionary<int, int>();
public bool ContainsTriangle(int v0, int v1, int v2)
{
if (!m_vertexIndexMap.ContainsKey(v0))
{
return false;
}
if (!m_vertexIndexMap.ContainsKey(v1))
{
return false;
}
if (!m_vertexIndexMap.ContainsKey(v2))
{
return false;
}
return true;
}

readonly List<Vector3> m_positions;
readonly List<Vector3> m_normals;
readonly List<Vector2> m_uv;
Expand All @@ -50,22 +74,25 @@ class VertexBuffer
readonly List<UShort4> m_joints;
readonly List<Vector4> m_weights;

public VertexBuffer(int reserve, Func<int, int> getJointIndex)
public VertexBuffer(int vertexCount, Func<int, int> getJointIndex)
{
m_positions = new List<Vector3>(reserve);
m_normals = new List<Vector3>(reserve);
m_positions = new List<Vector3>(vertexCount);
m_normals = new List<Vector3>(vertexCount);
m_uv = new List<Vector2>();

m_getJointIndex = getJointIndex;
if (m_getJointIndex != null)
{
m_joints = new List<UShort4>(reserve);
m_weights = new List<Vector4>(reserve);
m_joints = new List<UShort4>(vertexCount);
m_weights = new List<Vector4>(vertexCount);
}
}

public void Push(Vector3 position, Vector3 normal, Vector2 uv)
public void Push(int index, Vector3 position, Vector3 normal, Vector2 uv)
{
var newIndex = m_positions.Count;
m_vertexIndexMap.Add(index, newIndex);

m_positions.Add(position);
m_normals.Add(normal);
m_uv.Add(uv);
Expand All @@ -77,9 +104,9 @@ public void Push(BoneWeight boneWeight)
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)
public glTFPrimitives ToGltfPrimitive(glTF gltf, int bufferIndex, int materialIndex, IEnumerable<int> indices)
{
var indicesAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, indices.ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER);
var indicesAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, indices.Select(x => (uint)m_vertexIndexMap[x]).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);
Expand Down Expand Up @@ -109,6 +136,7 @@ public glTFPrimitives ToGltf(glTF gltf, int bufferIndex, int materialIndex, IRea
material = materialIndex,
mode = 4,
};

return primitive;
}
}
Expand Down Expand Up @@ -148,22 +176,14 @@ public static glTFMesh Export(glTF gltf, int bufferIndex,
// 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());
buffer.Push(k, axisInverter.InvertVector3(positions[k]), axisInverter.InvertVector3(normals[k]), uv[k].ReverseUV());
if (getJointIndex != null)
{
buffer.Push(boneWeights[k]);
Expand All @@ -177,16 +197,25 @@ public static glTFMesh Export(glTF gltf, int bufferIndex,
{
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>();
var flipped = new List<int>();
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 t0 = indices[j];
var t1 = indices[j + 1];
var t2 = indices[j + 2];
if (buffer.ContainsTriangle(t0, t1, t2))
{
flipped.Add(t2);
flipped.Add(t1);
flipped.Add(t0);
}
else
{
Debug.LogWarning($"triangle not contains [{t0}, {t1}, {t2}]");
}
}
var gltfPrimitive = buffer.ToGltf(gltf, bufferIndex, materialIndex, flipped);
var gltfPrimitive = buffer.ToGltfPrimitive(gltf, bufferIndex, materialIndex, flipped);

// blendShape
for (int j = 0; j < mesh.blendShapeCount; ++j)
Expand Down
86 changes: 86 additions & 0 deletions Assets/VRM/Tests/VrmDividedMeshTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NUnit.Framework;
using UniGLTF;
using UnityEngine;
using VRMShaders;

namespace VRM
{
public class DividedMeshTests
{
static string AliciaPath
{
get
{
return Path.GetFullPath(Application.dataPath + "/../Tests/Models/Alicia_vrm-0.51/AliciaSolid_vrm-0.51.vrm")
.Replace("\\", "/");
}
}

static GameObject Load(byte[] bytes, string path)
{
var parser = new GltfParser();
parser.Parse(path, bytes);

using (var loader = new VRMImporterContext(parser))
{
loader.Load();
loader.ShowMeshes();
return loader.DisposeOnGameObjectDestroyed().gameObject;
}
}

static IEnumerable<Mesh> GetMeshes(GameObject gameObject)
{
foreach (var r in gameObject.GetComponentsInChildren<Renderer>())
{
if (r is SkinnedMeshRenderer smr)
{
yield return smr.sharedMesh;
}
else if (r is MeshRenderer mr)
{
yield return r.GetComponent<MeshFilter>().sharedMesh;
}
}
}

/// <summary>
/// positions: [
/// {1, 1, 0}
/// {1, 1, 1}
/// {1, 1, 2}
/// {1, 1, 3}
/// {1, 1, 4}
/// {1, 1, 5}
/// ]
/// submesh
/// 0 1 2
/// submesh
/// 3 4 5
/// </summary>
[Test]
public void ExportDividedMeshTest()
{
var path = AliciaPath;
var loaded = Load(File.ReadAllBytes(path), path);

var exported = VRMExporter.Export(new UniGLTF.MeshExportSettings
{
DivideVertexBuffer = true, // test this
ExportOnlyBlendShapePosition = true,
ExportTangents = false,
UseSparseAccessorForMorphTarget = true,
}, loaded, AssetTextureUtil.IsTextureEditorAsset, AssetTextureUtil.GetTextureBytesWithMime);
var bytes = exported.ToGlbBytes();
var divided = Load(bytes, path);

var src = GetMeshes(loaded).ToArray();
var div = GetMeshes(divided).ToArray();

Assert.AreEqual(src[0].triangles.Length, div[0].triangles.Length);
}
}
}
11 changes: 11 additions & 0 deletions Assets/VRM/Tests/VrmDividedMeshTests.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.