Skip to content

Commit 0dfca1c

Browse files
committed
Fixed issue setting node and mesh morph weights.
1 parent 241d542 commit 0dfca1c

File tree

3 files changed

+115
-42
lines changed

3 files changed

+115
-42
lines changed

src/SharpGLTF.Core/Schema2/_Extensions.MorphWeights.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ public static void SetMorphWeights(this IList<Double> list, int maxCount, Transf
2424
list[i] = 0;
2525
}
2626

27-
foreach (var (index, weight) in weights.GetIndexedWeights())
28-
{
27+
foreach (var (index, weight) in weights.GetNonZeroWeights())
28+
{
2929
list[index] = weight;
3030
}
3131
}

src/SharpGLTF.Core/Transforms/SparseWeight8.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,18 @@ public IEnumerable<float> Expand(int count)
539539
yield return (Index7, Weight7);
540540
}
541541

542+
public IEnumerable<(int Index, float Weight)> GetNonZeroWeights()
543+
{
544+
if (Weight0 != 0) yield return (Index0, Weight0);
545+
if (Weight1 != 0) yield return (Index1, Weight1);
546+
if (Weight2 != 0) yield return (Index2, Weight2);
547+
if (Weight3 != 0) yield return (Index3, Weight3);
548+
if (Weight4 != 0) yield return (Index4, Weight4);
549+
if (Weight5 != 0) yield return (Index5, Weight5);
550+
if (Weight6 != 0) yield return (Index6, Weight6);
551+
if (Weight7 != 0) yield return (Index7, Weight7);
552+
}
553+
542554
public static SparseWeight8 Blend(ReadOnlySpan<SparseWeight8> sparses, ReadOnlySpan<float> weight)
543555
{
544556
var r = default(SparseWeight8);
@@ -673,17 +685,7 @@ Func<float, float, float, float, float> operationFunc
673685
return new SparseWeight8(rrr);
674686
}
675687

676-
internal IEnumerable<(int Index, float Weight)> GetNonZeroWeights()
677-
{
678-
if (Weight0 != 0) yield return (Index0, Weight0);
679-
if (Weight1 != 0) yield return (Index1, Weight1);
680-
if (Weight2 != 0) yield return (Index2, Weight2);
681-
if (Weight3 != 0) yield return (Index3, Weight3);
682-
if (Weight4 != 0) yield return (Index4, Weight4);
683-
if (Weight5 != 0) yield return (Index5, Weight5);
684-
if (Weight6 != 0) yield return (Index6, Weight6);
685-
if (Weight7 != 0) yield return (Index7, Weight7);
686-
}
688+
687689

688690
private float GetExpandedAt(int idx)
689691
{

tests/SharpGLTF.Core.Tests/Schema2/Authoring/AdvancedCreationTests.cs

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,79 @@ namespace SharpGLTF.Schema2.Authoring
1111
{
1212
internal class AdvancedCreationTests
1313
{
14+
[Test(Description = "Creates a primary mesh, and then another mesh derived from the data of the first mesh")]
15+
public void CreateDerivedSparseMeshes()
16+
{
17+
TestContext.CurrentContext.AttachGltfValidatorLinks();
18+
19+
// create model
20+
var model = ModelRoot.CreateModel();
21+
22+
// create scene
23+
var scene = model.DefaultScene = model.UseScene("Default");
24+
25+
// create material
26+
var material = model
27+
.CreateMaterial("Default")
28+
.WithDefault(new Vector4(0, 1, 0, 1));
29+
30+
// create primary node
31+
var rnode = scene.CreateNode("Triangle Node");
32+
33+
// create primary mesh
34+
var rmesh = rnode.Mesh = model.CreateMesh("Triangle Mesh");
35+
36+
// create the vertex positions
37+
var positions = new[]
38+
{
39+
new Vector3(0, 10, 0),
40+
new Vector3(-10, -10, 0),
41+
new Vector3(10, -10, 0),
42+
};
43+
44+
// create an index buffer and fill it
45+
var indices = new[] { 0, 1, 2 };
46+
47+
// create primary mesh primitive
48+
var originalPrimitive = rmesh
49+
.CreatePrimitive().WithMaterial(material)
50+
.WithVertexAccessor("POSITION", positions, true) // notice the (true) to explicitly set ByteStride, which is required for shared BufferViews
51+
.WithIndicesAccessor(PrimitiveType.TRIANGLES, indices);
52+
53+
54+
// create derived node
55+
rnode = scene.CreateNode("derived Triangle Node");
56+
rnode.LocalTransform = new Transforms.AffineTransform(Quaternion.Identity, new Vector3(30, 0, 0));
57+
58+
// create a sparse accessor based on the primary mesh position accessor, and a sparse override.
59+
60+
var positionsOverride = new Dictionary<int, Vector3>();
61+
positionsOverride[0] = new Vector3(0, 30, 0);
62+
63+
var sparseAccessor = model.CreateAccessor();
64+
sparseAccessor.SetDataFrom(originalPrimitive.GetVertexAccessor("POSITION")); // set base positions from original primitive
65+
sparseAccessor.CreateSparseData(positionsOverride);
66+
67+
// create derived mesh
68+
var dmesh = rnode.Mesh = model.CreateMesh("derived Triangle Mesh");
69+
70+
var derivedPrimitive = dmesh.CreatePrimitive().WithMaterial(material);
71+
derivedPrimitive.SetVertexAccessor("POSITION", sparseAccessor);
72+
derivedPrimitive.SetIndexAccessor(originalPrimitive.IndexAccessor);
73+
74+
// save model
75+
76+
model.AttachToCurrentTest("result.glb");
77+
model.AttachToCurrentTest("result.gltf");
78+
79+
var m1array = sparseAccessor.AsVector3Array();
80+
Assert.That(m1array[0], Is.EqualTo(positionsOverride[0]));
81+
Assert.That(m1array[1], Is.EqualTo(positions[1]));
82+
Assert.That(m1array[2], Is.EqualTo(positions[2]));
83+
}
84+
1485
[Test(Description = "Creates a model with a triangle mesh")]
15-
public void CreateSceneWithSparseAccessors()
86+
public void CreateMorphTargetsWithSparseAccessors()
1687
{
1788
TestContext.CurrentContext.AttachGltfValidatorLinks();
1889

@@ -47,41 +118,27 @@ public void CreateSceneWithSparseAccessors()
47118
// create mesh primitive
48119
var primitive = rmesh
49120
.CreatePrimitive()
50-
.WithVertexAccessor("POSITION", positions, true) // (true) notice here that we request creating a BufferView with explicit ByteStride
121+
.WithVertexAccessor("POSITION", positions)
51122
.WithIndicesAccessor(PrimitiveType.TRIANGLES, indices)
52123
.WithMaterial(material);
53124

54125
// create morph target 0
55-
56-
var morphs0Pos = new Dictionary<int, Vector3>(); // <VertexIndex, Position>
57-
morphs0Pos[2] = new Vector3(15, -15, 0);
58-
59-
var morphs0Acc = model.CreateAccessor();
60-
morphs0Acc.SetDataFrom(primitive.GetVertexAccessor("POSITION")); // set base data from position attribute accessor
61-
morphs0Acc.CreateSparseData(morphs0Pos);
62126

63-
var morphs0 = new Dictionary<string, Accessor>();
64-
morphs0["POSITION"] = morphs0Acc;
65-
66-
primitive.SetMorphTargetAccessors(0, morphs0);
127+
var morphs0Pos = new Dictionary<int, Vector3>(); // <VertexIndex, PositionDelta>
128+
morphs0Pos[2] = new Vector3(5, -5, 0);
129+
var morphs0Acc = _SetMorphTarget(primitive, 0, morphs0Pos);
67130

68131
// create morph target 1
69132

70-
var morphs1Pos = new Dictionary<int, Vector3>(); // <VertexIndex, Position>
71-
morphs1Pos[0] = new Vector3(0, 15, 0);
72-
73-
var morphs1Acc = model.CreateAccessor();
74-
morphs1Acc.SetDataFrom(primitive.GetVertexAccessor("POSITION")); // set base data from position attribute accessor
75-
morphs1Acc.CreateSparseData(morphs1Pos);
76-
77-
var morphs1 = new Dictionary<string, Accessor>();
78-
morphs1["POSITION"] = morphs1Acc;
79-
80-
primitive.SetMorphTargetAccessors(1, morphs1);
133+
var morphs1Pos = new Dictionary<int, Vector3>(); // <VertexIndex, PositionDelta>
134+
morphs1Pos[0] = new Vector3(0, 10, 0);
135+
var morphs1Acc = _SetMorphTarget(primitive, 1, morphs1Pos);
81136

82137
// initialize node with a specific weights blend
138+
139+
rnode.SetMorphWeights(Transforms.SparseWeight8.Create((0, 0.95f), (1, 0.75f))); // morph 0 at 0.95, morph 1 at 0.75
83140

84-
rnode.SetMorphWeights(Transforms.SparseWeight8.Create( (0, 0.95f), (1, 0.75f) )); // morph 0 at 0.95, morph 1 at 0.75
141+
Assert.That(rnode.MorphWeights, Is.EqualTo(new float[] { 0.95f, 0.75f }));
85142

86143
// save model
87144

@@ -91,15 +148,29 @@ public void CreateSceneWithSparseAccessors()
91148
// check accessors
92149

93150
var m0array = morphs0Acc.AsVector3Array();
94-
Assert.That(m0array[0], Is.EqualTo(positions[0]));
95-
Assert.That(m0array[1], Is.EqualTo(positions[1]));
151+
Assert.That(m0array[0], Is.EqualTo(Vector3.Zero));
152+
Assert.That(m0array[1], Is.EqualTo(Vector3.Zero));
96153
Assert.That(m0array[2], Is.EqualTo(morphs0Pos[2]));
97154

98155
var m1array = morphs1Acc.AsVector3Array();
99156
Assert.That(m1array[0], Is.EqualTo(morphs1Pos[0]));
100-
Assert.That(m1array[1], Is.EqualTo(positions[1]));
101-
Assert.That(m1array[2], Is.EqualTo(positions[2]));
157+
Assert.That(m1array[1], Is.EqualTo(Vector3.Zero));
158+
Assert.That(m1array[2], Is.EqualTo(Vector3.Zero));
159+
160+
}
161+
162+
private static Accessor _SetMorphTarget(MeshPrimitive primitive, int morphTargetIndex, Dictionary<int, Vector3> morphs1Pos)
163+
{
164+
var accessor = primitive.LogicalParent.LogicalParent.CreateAccessor();
165+
accessor.SetZeros(3, DimensionType.VEC3, EncodingType.FLOAT, false);
166+
accessor.CreateSparseData(morphs1Pos);
167+
168+
var attributes = new Dictionary<string, Accessor>();
169+
attributes["POSITION"] = accessor;
170+
171+
primitive.SetMorphTargetAccessors(morphTargetIndex, attributes);
102172

173+
return accessor;
103174
}
104175
}
105176
}

0 commit comments

Comments
 (0)