Merge pull request #1705 from ousttrue/fix/add_bone_weight

[BlendShape] Import 時に BoneWeight が0のMeshにBoneWeightを付与する
This commit is contained in:
ousttrue 2022-06-21 16:51:13 +09:00 committed by GitHub
commit 22a5263a72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 138 additions and 30 deletions

View File

@ -12,9 +12,10 @@ namespace UniGLTF
{
private NativeArray<MeshVertex> _vertices;
public NativeArray<MeshVertex> Vertices => _vertices.GetSubArray(0, _currentVertexCount);
private NativeArray<SkinnedMeshVertex> _skinnedMeshVertices;
public NativeArray<SkinnedMeshVertex> SkinnedMeshVertices => _skinnedMeshVertices.GetSubArray(0, _currentVertexCount);
int _currentVertexCount = 0;
private NativeArray<SkinnedMeshVertex> _skinnedMeshVertices;
public NativeArray<SkinnedMeshVertex> SkinnedMeshVertices => _skinnedMeshVertices.GetSubArray(0, _currentSkinCount);
int _currentSkinCount = 0;
private NativeArray<int> _indices;
public NativeArray<int> Indices => _indices.GetSubArray(0, _currentIndexCount);
@ -29,6 +30,7 @@ namespace UniGLTF
public bool HasNormal { get; private set; } = true;
public string Name { get; private set; }
public bool AssignBoneWeight { get; private set; }
public MeshData(int vertexCapacity, int indexCapacity)
{
@ -47,16 +49,19 @@ namespace UniGLTF
void Clear()
{
_currentVertexCount = 0;
_currentSkinCount = 0;
_currentIndexCount = 0;
_subMeshes.Clear();
_materialIndices.Clear();
_blendShapes.Clear();
Name = null;
HasNormal = false;
AssignBoneWeight = false;
}
/// <summary>
/// バッファ共有方式の判定
/// バッファ共有方式(vrm-0.x)の判定。
/// import の後方互換性のためで、vrm-1.0 export では使いません。
///
/// * バッファ共用方式は VertexBuffer が同じでSubMeshの index buffer がスライドしていく方式
/// * バッファがひとつのとき
@ -120,8 +125,8 @@ namespace UniGLTF
}
/// <summary>
/// * flip triangle
/// * add submesh offset
/// * flip triangle(gltfとtriangleの CW と CCW が異なる)
/// * add submesh offset(gltfのprimitiveは、頂点バッファが分かれているので連結。連結すると index が変わる(offset))
/// </summary>
private void PushIndices(BufferAccessor src, int offset)
{
@ -240,10 +245,14 @@ namespace UniGLTF
private void DropUnusedVertices()
{
Profiler.BeginSample("MeshData.DropUnusedVertices");
var maxIndex = _indices.Max();
var maxIndex = Indices.Max();
if (maxIndex + 1 < _currentVertexCount)
{
_currentIndexCount = maxIndex + 1;
_currentVertexCount = maxIndex + 1;
}
if (maxIndex + 1 < _currentSkinCount)
{
_currentSkinCount = maxIndex + 1;
}
foreach (var blendShape in _blendShapes)
{
@ -251,7 +260,6 @@ namespace UniGLTF
Truncate(blendShape.Normals, maxIndex);
Truncate(blendShape.Tangents, maxIndex);
}
Profiler.EndSample();
}
@ -279,16 +287,18 @@ namespace UniGLTF
}
}
private void AddVertex(MeshVertex vertex, SkinnedMeshVertex? skin)
private void AddVertex(MeshVertex vertex)
{
_vertices[_currentVertexCount] = vertex;
if (skin.HasValue)
{
_skinnedMeshVertices[_currentVertexCount] = skin.Value;
}
_currentVertexCount += 1;
}
private void AddSkin(SkinnedMeshVertex skin)
{
_skinnedMeshVertices[_currentSkinCount] = skin;
_currentSkinCount += 1;
}
/// <summary>
/// 各 primitive の attribute の要素が同じでない。=> uv が有るものと無いものが混在するなど
/// glTF 的にはありうる。
@ -314,6 +324,7 @@ namespace UniGLTF
var texCoords1 = primitives.GetTexCoords1(data, positions.Length);
var colors = primitives.GetColors(data, positions.Length);
var skinning = SkinningInfo.Create(data, gltfMesh, primitives);
AssignBoneWeight = skinning.ShouldSetRendererNodeAsBone ;
CheckAttributeUsages(primitives);
@ -348,8 +359,12 @@ namespace UniGLTF
texCoord0,
texCoord1,
color
),
skinning.GetSkinnedVertex(i));
));
var skin = skinning.GetSkinnedVertex(i);
if (skin.HasValue)
{
AddSkin(skin.Value);
}
}
// blendshape
@ -441,6 +456,7 @@ namespace UniGLTF
var texCoords1 = primitives.GetTexCoords1(data, positions.Length);
var colors = primitives.GetColors(data, positions.Length);
var skinning = SkinningInfo.Create(data, gltfMesh, primitives);
AssignBoneWeight = skinning.ShouldSetRendererNodeAsBone ;
CheckAttributeUsages(primitives);
@ -472,8 +488,13 @@ namespace UniGLTF
normal,
texCoord0,
texCoord1,
color),
skinning.GetSkinnedVertex(i));
color));
var skin =
skinning.GetSkinnedVertex(i);
if (skin.HasValue)
{
AddSkin(skin.Value);
}
}
// blendshape

View File

@ -34,6 +34,10 @@ namespace UniGLTF
var normals = mesh.normals;
var uv = mesh.uv;
var boneWeights = mesh.boneWeights;
if (boneWeights.All(x => x.weight0 == 0 && x.weight1 == 0 && x.weight2 == 0 && x.weight3 == 0))
{
boneWeights = null;
}
var colors = mesh.colors;
Func<int, int> getJointIndex = null;

View File

@ -57,15 +57,23 @@ namespace UniGLTF
colorAccessorIndex = data.ExtendBufferAndGetAccessorIndex(mesh.colors, glBufferTarget.ARRAY_BUFFER);
}
var boneweights = mesh.boneWeights;
var weightAccessorIndex = data.ExtendBufferAndGetAccessorIndex(boneweights.Select(y => new Vector4(y.weight0, y.weight1, y.weight2, y.weight3)).ToArray(), glBufferTarget.ARRAY_BUFFER);
var 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 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
{

View File

@ -137,7 +137,8 @@ namespace UniGLTF
var result = new MeshWithMaterials
{
Mesh = mesh,
Materials = data.MaterialIndices.Select(materialFromIndex).ToArray()
Materials = data.MaterialIndices.Select(materialFromIndex).ToArray(),
ShouldSetRendererNodeAsBone = data.AssignBoneWeight,
};
await awaitCaller.NextFrame();

View File

@ -9,7 +9,12 @@ namespace UniGLTF
public Mesh Mesh;
public Material[] Materials;
/// <summary>
/// SkinningInfo.ShouldSetRendererNodeAsBone を伝播させる。
/// </summary>
public bool ShouldSetRendererNodeAsBone = false;
// 複数のノードから参照されうる
public List<Renderer> Renderers=new List<Renderer>(); // SkinnedMeshRenderer or MeshRenderer
public List<Renderer> Renderers = new List<Renderer>(); // SkinnedMeshRenderer or MeshRenderer
}
}

View File

@ -5,14 +5,69 @@ namespace UniGLTF
public JointsAccessor.Getter Joints;
public WeightsAccessor.Getter Weights;
/// <summary>
/// gltfMesh に morphTarget が有る場合に、Unity では boneWeight の有無と無関係に UnityEngine.SkinnedMeshRenderer を使います。
/// そのため `boneWeight が無い` UnityEngine.SkinnedMeshRenderer となる場合があります。
/// boneWeight の無い SkinnedMeshRenderer の rootBone は、
///
/// * boundingBox の中心
/// * boneWeight の無いボーンに対するスキニング
///
/// が兼用になるためメッシュが正しく描画されない場合があります。
/// この問題の対策として、全頂点に対して boneWeight = 1, boneJoint = 0 を付与します。
/// この boneWeight を利用するために AddComponent<SkinnedMeshRenderer> する段階で、
///
/// * mesh.bindMatrices
/// * SkinnedMeshRenderer.bones = new Transform[]{ renderer.transform };
///
/// が必要です。この変数はその指示です。
/// </summary>
public bool ShouldSetRendererNodeAsBone { get; private set; }
public static SkinningInfo Create(GltfData data, glTFMesh mesh, glTFPrimitives primitives)
{
var hasMorphTarget = HasMorphTarget(mesh);
var positions = data.GLTF.accessors[primitives.attributes.POSITION];
return new SkinningInfo
var skinning = new SkinningInfo
{
Joints = primitives.GetJoints(data, positions.count),
Weights = primitives.GetWeights(data, positions.count),
};
if (skinning.Joints != null)
{
// use SkinnedMeshRenderer
return skinning;
}
else if (!hasMorphTarget)
{
// use MeshRenderer
return skinning;
}
else
{
// use SkinnedMeshRenderer without boneWeight.
// https://github.com/vrm-c/UniVRM/issues/1675
return new SkinningInfo
{
ShouldSetRendererNodeAsBone = true,
Joints = _ => (0, 0, 0, 0),
Weights = _ => (1, 0, 0, 0), // assign weight 1
};
}
}
static bool HasMorphTarget(glTFMesh mesh)
{
foreach (var prim in mesh.primitives)
{
if (prim.targets != null && prim.targets.Count > 0)
{
return true;
}
}
return false;
}
private static (float x, float y, float z, float w) NormalizeBoneWeight(
@ -32,7 +87,7 @@ namespace UniGLTF
return src;
}
public SkinnedMeshVertex GetSkinnedVertex(int i)
public SkinnedMeshVertex? GetSkinnedVertex(int i)
{
if (Joints == null)
{

View File

@ -119,6 +119,20 @@ namespace UniGLTF
renderer.sharedMaterials = mesh.Materials;
// invisible in loading
renderer.enabled = false;
if (mesh.ShouldSetRendererNodeAsBone )
{
renderer.bones = new[] { renderer.transform };
//
// calc default matrices
// https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html
//
var meshCoords = renderer.transform;
var calculatedBindPoses = renderer.bones.Select(bone => bone.worldToLocalMatrix * meshCoords.localToWorldMatrix).ToArray();
mesh.Mesh.bindposes = calculatedBindPoses;
}
mesh.Renderers.Add(renderer);
}
}