Skip to content

Commit c71bf2c

Browse files
committed
Skin: Exposed Joints and IBMs as individual lists, fixes vpenades#244
1 parent 2261a0d commit c71bf2c

File tree

2 files changed

+90
-25
lines changed

2 files changed

+90
-25
lines changed

src/SharpGLTF.Core/Schema2/gltf.Accessors.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ public IList<Matrix4x4> AsMatrix4x4Array()
244244
return _GetMemoryAccessor().AsMatrix4x4Array();
245245
}
246246

247+
internal IReadOnlyList<Matrix4x4> AsMatrix4x4ReadOnlyList()
248+
{
249+
return _GetMemoryAccessor().AsMatrix4x4Array();
250+
}
251+
247252
#endregion
248253

249254
#region Index Buffer API

src/SharpGLTF.Core/Schema2/gltf.Skin.cs

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ namespace SharpGLTF.Schema2
88
[System.Diagnostics.DebuggerDisplay("Skin[{LogicalIndex}] {Name}")]
99
public sealed partial class Skin
1010
{
11+
// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#skins
12+
1113
// https://github.com/KhronosGroup/glTF/issues/461
1214
// https://github.com/KhronosGroup/glTF/issues/100
1315
// https://github.com/KhronosGroup/glTF/issues/403
@@ -33,32 +35,88 @@ internal Skin()
3335
public IEnumerable<Node> VisualParents => Node.FindNodesUsingSkin(this);
3436

3537
/// <summary>
36-
/// Gets the number of joints
38+
/// Gets the number of joints.
3739
/// </summary>
3840
public int JointsCount => _joints.Count;
3941

4042
/// <summary>
41-
/// Gets or sets the Skeleton <see cref="Node"/>, which represents the root of a joints hierarchy.
43+
/// Gets the list of joints.
44+
/// </summary>
45+
/// <remarks>
46+
/// Each joint is correlated to its respective IBM in <see cref="InverseBindMatrices"/>.
47+
/// </remarks>
48+
public IReadOnlyList<Node> Joints => _joints.SelectList(idx => this.LogicalParent.LogicalNodes[idx]);
49+
50+
/// <summary>
51+
/// Gets the list of Inverse Bind Matrices.
52+
/// </summary>
53+
/// <remarks>
54+
/// Each IBM is correlated to its respective Joint in <see cref="Joints"/>.
55+
/// </remarks>
56+
public IReadOnlyList<Matrix4x4> InverseBindMatrices
57+
{
58+
get
59+
{
60+
var matrices = GetInverseBindMatricesAccessor();
61+
if (matrices == null) return Array.Empty<Matrix4x4>();
62+
63+
System.Diagnostics.Debug.Assert(matrices.Count == _joints.Count, "IBM and Joints count mismatch");
64+
65+
return matrices.AsMatrix4x4ReadOnlyList();
66+
}
67+
}
68+
69+
/// <summary>
70+
/// Gets or sets the Skeleton <see cref="Node"/>, which represents the root of a joints hierarchy AKA the Armature Root.
4271
/// </summary>
72+
/// <remarks>
73+
/// <para>
74+
/// As per <see href="https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#skins-overview">glTF specification</see>,
75+
/// this value is optional. So don't expect all glTF models to have this property set.
76+
/// </para>
77+
/// <para>
78+
/// Although the skeleton property is not needed for computing skinning transforms, it may be used to provide a specific “pivot point” for the skinned geometry.
79+
/// </para>
80+
/// </remarks>
4381
public Node Skeleton
4482
{
45-
get => this._skeleton.HasValue ? this.LogicalParent.LogicalNodes[this._skeleton.Value] : null;
83+
get
84+
{
85+
return this._skeleton.HasValue
86+
? this.LogicalParent.LogicalNodes[this._skeleton.Value]
87+
: null;
88+
}
89+
4690
set
4791
{
4892
if (value != null) Guard.MustShareLogicalParent(this.LogicalParent, nameof(this.LogicalParent), value, nameof(value));
49-
this._skeleton = value == null ? (int?)null : value.LogicalIndex;
93+
this._skeleton = value == null
94+
? (int?)null
95+
: value.LogicalIndex;
5096
}
5197
}
5298

5399
#endregion
54100

55101
#region API
56102

57-
public Accessor GetInverseBindMatricesAccessor()
103+
public Accessor UseInverseBindMatricesAccessor()
58104
{
59-
if (!this._inverseBindMatrices.HasValue) return null;
105+
var accessor = GetInverseBindMatricesAccessor();
106+
if (accessor == null)
107+
{
108+
accessor = LogicalParent.CreateAccessor("Bind Matrices");
109+
this._inverseBindMatrices = accessor.LogicalIndex;
110+
}
111+
112+
return accessor;
113+
}
60114

61-
return this.LogicalParent.LogicalAccessors[this._inverseBindMatrices.Value];
115+
public Accessor GetInverseBindMatricesAccessor()
116+
{
117+
return _inverseBindMatrices.HasValue
118+
? this.LogicalParent.LogicalAccessors[this._inverseBindMatrices.Value]
119+
: null;
62120
}
63121

64122
public (Node Joint, Matrix4x4 InverseBindMatrix) GetJoint(int idx)
@@ -69,7 +127,9 @@ public Accessor GetInverseBindMatricesAccessor()
69127

70128
var matrices = GetInverseBindMatricesAccessor();
71129

72-
var matrix = matrices == null ? Matrix4x4.Identity : matrices.AsMatrix4x4Array()[idx];
130+
var matrix = matrices == null
131+
? Matrix4x4.Identity
132+
: matrices.AsMatrix4x4Array()[idx];
73133

74134
return (node, matrix);
75135
}
@@ -82,7 +142,7 @@ public void BindJoints(params Node[] joints)
82142
}
83143

84144
/// <summary>
85-
/// Binds a bone armature of <see cref="Node"/> to the associated skinned mesh.
145+
/// Binds the armature <see cref="Node"/>s to the associated skinned mesh.
86146
/// </summary>
87147
/// <param name="meshBindTransform">The world transform matrix of the mesh at the time of binding.</param>
88148
/// <param name="joints">A collection of <see cref="Node"/> joints.</param>
@@ -114,47 +174,47 @@ public void BindJoints(Matrix4x4 meshBindTransform, params Node[] joints)
114174
/// A collection of <see cref="Node"/> joints,
115175
/// where each joint has an Inverse Bind Matrix.
116176
/// </param>
117-
public void BindJoints((Node Joint, Matrix4x4 InverseBindMatrix)[] joints)
177+
public void BindJoints(IReadOnlyList<(Node Joint, Matrix4x4 InverseBindMatrix)> joints)
118178
{
119179
Guard.NotNull(joints, nameof(joints));
120180

121181
_FindCommonAncestor(joints.Select(item => item.Joint));
182+
183+
// sanitize IBMs
122184

123-
// Acording to gltf schema
124-
// "The fourth row of each matrix MUST be set to [0.0, 0.0, 0.0, 1.0]."
125-
// https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skins-overview
126-
for (int i = 0; i < joints.Length; ++i)
185+
Matrix4x4 _SanitizedIBM(Matrix4x4 ibm, int idx)
127186
{
128-
var ibm = joints[i].InverseBindMatrix;
187+
Transforms.Matrix4x4Factory.GuardMatrix($"{nameof(joints)}[{idx}]", ibm, Transforms.Matrix4x4Factory.MatrixCheck.InverseBindMatrix, 0.01f);
129188

130-
Transforms.Matrix4x4Factory.GuardMatrix($"{nameof(joints)}[{i}]", ibm, Transforms.Matrix4x4Factory.MatrixCheck.InverseBindMatrix, 0.01f);
189+
// Acording to gltf specs https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skins-overview
190+
// "The fourth row of each matrix MUST be set to [0.0, 0.0, 0.0, 1.0]."
131191

132-
// fourth column (row in schema) is within tolerance
133-
// so we can enforce exact values,
192+
// fourth column (row in schema) has passed the guard and it is within tolerance so we can enforce exact values,
134193
ibm.M14 = 0;
135194
ibm.M24 = 0;
136195
ibm.M34 = 0;
137196
ibm.M44 = 1;
138197

139-
joints[i] = (joints[i].Joint, ibm);
198+
return ibm;
140199
}
141200

201+
var ibms = joints.Select((item, idx) => _SanitizedIBM(item.InverseBindMatrix, idx));
202+
142203
// inverse bind matrices accessor
143204

144-
var data = new Byte[joints.Length * 16 * 4];
205+
var data = new Byte[joints.Count * 16 * 4];
145206
var matrices = new Memory.Matrix4x4Array(data, 0, EncodingType.FLOAT, false);
146-
matrices.Fill(joints.Select(item => item.InverseBindMatrix));
207+
matrices.Fill(ibms);
147208

148-
var accessor = LogicalParent.CreateAccessor("Bind Matrices");
149-
accessor.SetData( LogicalParent.UseBufferView(data), 0, joints.Length, DimensionType.MAT4, EncodingType.FLOAT, false);
209+
var ibmsView = LogicalParent.UseBufferView(data);
150210

151-
this._inverseBindMatrices = accessor.LogicalIndex;
211+
UseInverseBindMatricesAccessor().SetData(ibmsView, 0, joints.Count, DimensionType.MAT4, EncodingType.FLOAT, false);
152212

153213
// joints
154214

155215
_joints.Clear();
156216
_joints.AddRange(joints.Select(item => item.Joint.LogicalIndex));
157-
}
217+
}
158218

159219
#endregion
160220

0 commit comments

Comments
 (0)