mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-20 01:37:59 -05:00
Merge pull request #1705 from ousttrue/fix/add_bone_weight
[BlendShape] Import 時に BoneWeight が0のMeshにBoneWeightを付与する
This commit is contained in:
commit
22a5263a72
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user