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

Feature/unique joints #668

Merged
merged 9 commits into from
Jan 14, 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
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ public enum ExportValidatorMessages
[LangMsg(Languages.en, "ExportRoot must be topmost parent")]
NO_PARENT,

[LangMsg(Languages.ja, "ヒエラルキーに active なメッシュが含まれていない")]
[LangMsg(Languages.ja, "ヒエラルキーに active なメッシュが含まれていません")]
[LangMsg(Languages.en, "No active mesh")]
NO_ACTIVE_MESH,

[LangMsg(Languages.ja, "ヒエラルキーの中に同じ名前のGameObjectが含まれている。 エクスポートした場合に自動でリネームする")]
[LangMsg(Languages.ja, "ヒエラルキーの中に同じ名前のGameObjectが含まれている。 エクスポートした場合に自動でリネームします")]
[LangMsg(Languages.en, "There are bones with the same name in the hierarchy. They will be automatically renamed after export")]
DUPLICATE_BONE_NAME_EXISTS,

[LangMsg(Languages.ja, "SkinnedMeshRenderer.bones に重複する内容がある。エクスポートした場合に、bones の重複を取り除き、boneweights, bindposes を調整します")]
[LangMsg(Languages.en, "There are same bone in bones the SkinnedMeshRenderer. They will be automatically uniqued")]
NO_UNIQUE_JOINTS,
}

/// <summary>
Expand All @@ -45,6 +49,19 @@ static bool DuplicateNodeNameExists(GameObject ExportRoot)
return (duplicates.Any());
}

static bool HasNoUniqueJoints(Renderer r)
{
if (r is SkinnedMeshRenderer skin)
{
if (skin.bones != null && skin.bones.Length != skin.bones.Distinct().Count())
{
return true;
}
}

return false;
}

public static IEnumerable<Validation> Validate(GameObject ExportRoot)
{
if (ExportRoot == null)
Expand All @@ -66,6 +83,11 @@ public static IEnumerable<Validation> Validate(GameObject ExportRoot)
yield break;
}

if (renderers.Any(x => HasNoUniqueJoints(x)))
{
yield return Validation.Warning(ExportValidatorMessages.NO_UNIQUE_JOINTS.Msg());
}

if (DuplicateNodeNameExists(ExportRoot))
{
yield return Validation.Warning(ExportValidatorMessages.DUPLICATE_BONE_NAME_EXISTS.Msg());
Expand Down
42 changes: 17 additions & 25 deletions Assets/UniGLTF/Runtime/UniGLTF/IO/MeshExporter.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniJSON;
using UnityEngine;

namespace UniGLTF
Expand All @@ -24,10 +23,10 @@ public struct MeshExportSettings
public static class MeshExporter
{
static glTFMesh ExportPrimitives(glTF gltf, int bufferIndex,
string rendererName,
Mesh mesh, Material[] materials,
List<Material> unityMaterials)
MeshWithRenderer unityMesh, List<Material> unityMaterials)
{
var mesh = unityMesh.Mesh;
var materials = unityMesh.Renderer.sharedMaterials;
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();
Expand All @@ -53,7 +52,13 @@ static glTFMesh ExportPrimitives(glTF gltf, int bufferIndex,

var boneweights = mesh.boneWeights;
var weightAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, boneweights.Select(y => new Vector4(y.weight0, y.weight1, y.weight2, y.weight3)).ToArray(), glBufferTarget.ARRAY_BUFFER);
var jointsAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, boneweights.Select(y => new UShort4((ushort)y.boneIndex0, (ushort)y.boneIndex1, (ushort)y.boneIndex2, (ushort)y.boneIndex3)).ToArray(), glBufferTarget.ARRAY_BUFFER);
var jointsAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, 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
{
Expand Down Expand Up @@ -98,7 +103,7 @@ static glTFMesh ExportPrimitives(glTF gltf, int bufferIndex,

if (j >= materials.Length)
{
Debug.LogWarningFormat("{0}.materials is not enough", rendererName);
Debug.LogWarningFormat("{0}.materials is not enough", unityMesh.Renderer.name);
break;
}

Expand Down Expand Up @@ -251,36 +256,23 @@ static gltfMorphTarget ExportMorphTarget(glTF gltf, int bufferIndex,
};
}

public struct MeshWithRenderer
{
public Mesh Mesh;
[Obsolete("Use Renderer")]
public Renderer Rendererer { get { return Renderer; } set { Renderer = value; } }
public Renderer Renderer;
}

public static IEnumerable<(Mesh, glTFMesh, Dictionary<int, int>)> ExportMeshes(glTF gltf, int bufferIndex,
List<MeshWithRenderer> unityMeshes, List<Material> unityMaterials,
MeshExportSettings settings)
{
for (int i = 0; i < unityMeshes.Count; ++i)
foreach (var unityMesh in unityMeshes)
{
var x = unityMeshes[i];
var mesh = x.Mesh;
var materials = x.Renderer.sharedMaterials;

var gltfMesh = ExportPrimitives(gltf, bufferIndex,
x.Renderer.name,
mesh, materials, unityMaterials);
unityMesh, unityMaterials);

var targetNames = new List<string>();

var blendShapeIndexMap = new Dictionary<int, int>();
int exportBlendShapes = 0;
for (int j = 0; j < mesh.blendShapeCount; ++j)
for (int j = 0; j < unityMesh.Mesh.blendShapeCount; ++j)
{
var morphTarget = ExportMorphTarget(gltf, bufferIndex,
mesh, j,
unityMesh.Mesh, j,
settings.UseSparseAccessorForMorphTarget,
settings.ExportOnlyBlendShapePosition);
if (morphTarget.POSITION < 0 && morphTarget.NORMAL < 0 && morphTarget.TANGENT < 0)
Expand All @@ -289,7 +281,7 @@ public struct MeshWithRenderer
}

// maybe skip
var blendShapeName = mesh.GetBlendShapeName(j);
var blendShapeName = unityMesh.Mesh.GetBlendShapeName(j);
blendShapeIndexMap.Add(j, exportBlendShapes++);
targetNames.Add(blendShapeName);

Expand All @@ -304,7 +296,7 @@ public struct MeshWithRenderer

gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames);

yield return (mesh, gltfMesh, blendShapeIndexMap);
yield return (unityMesh.Mesh, gltfMesh, blendShapeIndexMap);
}
}
}
Expand Down
89 changes: 89 additions & 0 deletions Assets/UniGLTF/Runtime/UniGLTF/IO/MeshWithRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace UniGLTF
{
public struct MeshWithRenderer
{
public readonly Mesh Mesh;
public readonly Renderer Renderer;
public readonly Transform[] UniqueBones;
readonly int[] JointIndexMap;

public MeshWithRenderer(Transform x)
{
Mesh = x.GetSharedMesh();
Renderer = x.GetComponent<Renderer>();

if (Renderer is SkinnedMeshRenderer skin && skin.bones != null && skin.bones.Length > 0)
{
// has joints
var uniqueBones = skin.bones.Distinct().ToArray();
UniqueBones = uniqueBones;
JointIndexMap = new int[skin.bones.Length];

var bones = skin.bones;
for (int i = 0; i < bones.Length; ++i)
{
JointIndexMap[i] = Array.IndexOf(uniqueBones, bones[i]);
}
}
else
{
UniqueBones = null;
JointIndexMap = null;
}
}

public int GetJointIndex(int index)
{
if (index < 0)
{
return index;
}

if (JointIndexMap != null)
{
return JointIndexMap[index];
}
else
{
return index;
}
}

public IEnumerable<Matrix4x4> GetBindPoses()
{
var used = new HashSet<int>();
for (int i = 0; i < JointIndexMap.Length; ++i)
{
var index = JointIndexMap[i];
if (used.Add(index))
{
yield return Mesh.bindposes[i];
}
}
}

public static IEnumerable<MeshWithRenderer> FromNodes(IEnumerable<Transform> nodes)
{
foreach (var node in nodes)
{
var x = new MeshWithRenderer(node);
if (x.Mesh == null)
{
continue; ;
}
if (x.Renderer.sharedMaterials == null
|| x.Renderer.sharedMaterials.Length == 0)
{
continue;
}

yield return x;
}
}
}
}
11 changes: 11 additions & 0 deletions Assets/UniGLTF/Runtime/UniGLTF/IO/MeshWithRenderer.cs.meta

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

40 changes: 9 additions & 31 deletions Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -252,27 +252,7 @@ public virtual void Export(MeshExportSettings meshExportSettings)
#endregion

#region Meshes
var unityMeshes = Nodes
.Select(x => new MeshExporter.MeshWithRenderer
{
Mesh = x.GetSharedMesh(),
Renderer = x.GetComponent<Renderer>(),
})
.Where(x =>
{
if (x.Mesh == null)
{
return false;
}
if (x.Renderer.sharedMaterials == null
|| x.Renderer.sharedMaterials.Length == 0)
{
return false;
}

return true;
})
.ToList();
var unityMeshes = MeshWithRenderer.FromNodes(Nodes).ToList();

MeshBlendShapeIndexMap = new Dictionary<Mesh, Dictionary<int, int>>();
foreach (var (mesh, gltfMesh, blendShapeIndexMap) in MeshExporter.ExportMeshes(
Expand All @@ -289,13 +269,10 @@ public virtual void Export(MeshExportSettings meshExportSettings)
#endregion

#region Nodes and Skins
var unitySkins = Nodes
.Select(x => x.GetComponent<SkinnedMeshRenderer>()).Where(x =>
x != null
&& x.bones != null
&& x.bones.Length > 0)
var unitySkins = unityMeshes
.Where(x => x.UniqueBones != null)
.ToList();
glTF.nodes = Nodes.Select(x => ExportNode(x, Nodes, unityMeshes.Select(y => y.Renderer).ToList(), unitySkins)).ToList();
glTF.nodes = Nodes.Select(x => ExportNode(x, Nodes, unityMeshes.Select(y => y.Renderer).ToList(), unitySkins.Select(y => y.Renderer as SkinnedMeshRenderer).ToList())).ToList();
glTF.scenes = new List<gltfScene>
{
new gltfScene
Expand All @@ -306,19 +283,20 @@ public virtual void Export(MeshExportSettings meshExportSettings)

foreach (var x in unitySkins)
{
var matrices = x.sharedMesh.bindposes.Select(y => y.ReverseZ()).ToArray();
var matrices = x.GetBindPoses().Select(y => y.ReverseZ()).ToArray();
var accessor = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, matrices, glBufferTarget.NONE);

var renderer = x.Renderer as SkinnedMeshRenderer;
var skin = new glTFSkin
{
inverseBindMatrices = accessor,
joints = x.bones.Select(y => Nodes.IndexOf(y)).ToArray(),
skeleton = Nodes.IndexOf(x.rootBone),
joints = x.UniqueBones.Select(y => Nodes.IndexOf(y)).ToArray(),
skeleton = Nodes.IndexOf(renderer.rootBone),
};
var skinIndex = glTF.skins.Count;
glTF.skins.Add(skin);

foreach (var z in Nodes.Where(y => y.Has(x)))
foreach (var z in Nodes.Where(y => y.Has(x.Renderer)))
{
var nodeIndex = Nodes.IndexOf(z);
var node = glTF.nodes[nodeIndex];
Expand Down
5 changes: 4 additions & 1 deletion Assets/VRM/Runtime/SkinnedMeshUtility/VRMBoneNormalizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ public static GameObject Execute(GameObject go, bool forceTPose)
.ToDictionary(x => x.Key, x => boneMap[x.Value])
;

var animator = dst.AddComponent<Animator>();
if (dst.GetComponent<Animator>() == null)
{
var animator = dst.AddComponent<Animator>();
}
var vrmHuman = go.GetComponent<VRMHumanoidDescription>();
var avatarDescription = AvatarDescription.Create();
if (vrmHuman != null && vrmHuman.Description != null)
Expand Down