mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-08-25 20:16:05 -05:00
Merge pull request #668 from ousttrue/feature/unique_joints
Feature/unique joints
This commit is contained in:
@@ -17,13 +17,17 @@ namespace MeshUtility.Validators
|
||||
[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>
|
||||
@@ -45,6 +49,19 @@ namespace MeshUtility.Validators
|
||||
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)
|
||||
@@ -66,6 +83,11 @@ namespace MeshUtility.Validators
|
||||
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());
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniJSON;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
@@ -24,10 +23,10 @@ namespace UniGLTF
|
||||
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();
|
||||
@@ -53,7 +52,13 @@ namespace UniGLTF
|
||||
|
||||
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
|
||||
{
|
||||
@@ -124,7 +129,7 @@ namespace UniGLTF
|
||||
|
||||
if (j >= materials.Length)
|
||||
{
|
||||
Debug.LogWarningFormat("{0}.materials is not enough", rendererName);
|
||||
Debug.LogWarningFormat("{0}.materials is not enough", unityMesh.Renderer.name);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -277,36 +282,23 @@ namespace UniGLTF
|
||||
};
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -315,7 +307,7 @@ namespace UniGLTF
|
||||
}
|
||||
|
||||
// maybe skip
|
||||
var blendShapeName = mesh.GetBlendShapeName(j);
|
||||
var blendShapeName = unityMesh.Mesh.GetBlendShapeName(j);
|
||||
blendShapeIndexMap.Add(j, exportBlendShapes++);
|
||||
targetNames.Add(blendShapeName);
|
||||
|
||||
@@ -330,7 +322,7 @@ namespace UniGLTF
|
||||
|
||||
gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames);
|
||||
|
||||
yield return (mesh, gltfMesh, blendShapeIndexMap);
|
||||
yield return (unityMesh.Mesh, gltfMesh, blendShapeIndexMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
89
Assets/UniGLTF/Runtime/UniGLTF/IO/MeshWithRenderer.cs
Normal file
89
Assets/UniGLTF/Runtime/UniGLTF/IO/MeshWithRenderer.cs
Normal file
@@ -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
Assets/UniGLTF/Runtime/UniGLTF/IO/MeshWithRenderer.cs.meta
Normal file
11
Assets/UniGLTF/Runtime/UniGLTF/IO/MeshWithRenderer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f745ee7743b23243bf6adcb740dab82
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -252,27 +252,7 @@ namespace UniGLTF
|
||||
#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(
|
||||
@@ -289,13 +269,10 @@ namespace UniGLTF
|
||||
#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
|
||||
@@ -306,19 +283,20 @@ namespace UniGLTF
|
||||
|
||||
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];
|
||||
|
||||
@@ -83,7 +83,10 @@ namespace VRM
|
||||
.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)
|
||||
|
||||
Reference in New Issue
Block a user