@@ -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