@@ -23,6 +23,7 @@ import {ModelViewerGLTFInstance} from '../../three-components/gltf-instance/Mode
23
23
import { waitForEvent } from '../../utilities.js' ;
24
24
import { assetPath , rafPasses } from '../helpers.js' ;
25
25
import { BasicSpecTemplate } from '../templates.js' ;
26
+ import { ModelScene } from "../../three-components/ModelScene" ;
26
27
27
28
28
29
@@ -31,11 +32,18 @@ const expect = chai.expect;
31
32
const ASTRONAUT_GLB_PATH = assetPath ( 'models/Astronaut.glb' ) ;
32
33
const HORSE_GLB_PATH = assetPath ( 'models/Horse.glb' ) ;
33
34
const CUBES_GLB_PATH = assetPath ( 'models/cubes.gltf' ) ; // has variants
35
+ const MESH_PRIMITIVES_GLB_PATH = assetPath ( 'models/MeshPrimitivesVariants.glb' ) ; // has variants
34
36
const CUBE_GLB_PATH = assetPath ( 'models/cube.gltf' ) ; // has UV coords
35
37
const SUNRISE_IMG_PATH = assetPath ( 'environments/spruit_sunrise_1k_LDR.jpg' ) ;
36
38
const RIGGEDFIGURE_GLB_PATH = assetPath (
37
39
'models/glTF-Sample-Models/2.0/RiggedFigure/glTF-Binary/RiggedFigure.glb' ) ;
38
40
41
+ function getGLTFRoot ( scene : ModelScene , hasBeenExportedOnce = false ) {
42
+ // TODO: export is putting in an extra node layer, because the loader
43
+ // gives us a Group, but if the exporter doesn't get a Scene, then it
44
+ // wraps everything in an "AuxScene" node. Feels like a three.js bug.
45
+ return hasBeenExportedOnce ? scene . modelContainer . children [ 0 ] . children [ 0 ] : scene . modelContainer . children [ 0 ] ;
46
+ }
39
47
40
48
suite ( 'ModelViewerElementBase with SceneGraphMixin' , ( ) => {
41
49
let nextId = 0 ;
@@ -88,13 +96,13 @@ suite('ModelViewerElementBase with SceneGraphMixin', () => {
88
96
test ( 'has variants' , ( ) => {
89
97
expect ( element [ $scene ] . currentGLTF ! . userData . variants . length )
90
98
. to . be . eq ( 3 ) ;
91
- const glTFroot = element [ $scene ] . modelContainer . children [ 0 ] ;
92
- expect ( glTFroot . children [ 0 ] . userData . variantMaterials . size ) . to . be . eq ( 3 ) ;
93
- expect ( glTFroot . children [ 1 ] . userData . variantMaterials . size ) . to . be . eq ( 3 ) ;
99
+ const gltfRoot = getGLTFRoot ( element [ $scene ] ) ;
100
+ expect ( gltfRoot . children [ 0 ] . userData . variantMaterials . size ) . to . be . eq ( 3 ) ;
101
+ expect ( gltfRoot . children [ 1 ] . userData . variantMaterials . size ) . to . be . eq ( 3 ) ;
94
102
} ) ;
95
103
96
104
test (
97
- `Setting varianName to null results in primitive
105
+ `Setting variantName to null results in primitive
98
106
reverting to default/initial material` ,
99
107
async ( ) => {
100
108
let primitiveNode : PrimitiveNode | null = null
@@ -122,7 +130,7 @@ suite('ModelViewerElementBase with SceneGraphMixin', () => {
122
130
. equal ( 'purple' ) ;
123
131
} ) ;
124
132
125
- test ( 'exports and reimports the model with variants' , async ( ) => {
133
+ test ( 'exports and re-imports the model with variants' , async ( ) => {
126
134
const exported = await element . exportScene ( { binary : true } ) ;
127
135
const url = URL . createObjectURL ( exported ) ;
128
136
element . src = url ;
@@ -131,12 +139,70 @@ suite('ModelViewerElementBase with SceneGraphMixin', () => {
131
139
132
140
expect ( element [ $scene ] . currentGLTF ! . userData . variants . length )
133
141
. to . be . eq ( 3 ) ;
134
- // TODO: export is putting in an extra node layer, because the loader
135
- // gives us a Group, but if the exporter doesn't get a Scene, then it
136
- // wraps everything in an "AuxScene" node. Feels like a three.js bug.
137
- const glTFroot = element [ $scene ] . modelContainer . children [ 0 ] . children [ 0 ] ;
138
- expect ( glTFroot . children [ 0 ] . userData . variantMaterials . size ) . to . be . eq ( 3 ) ;
139
- expect ( glTFroot . children [ 1 ] . userData . variantMaterials . size ) . to . be . eq ( 3 ) ;
142
+ const gltfRoot = getGLTFRoot ( element [ $scene ] , true ) ;
143
+ expect ( gltfRoot . children [ 0 ] . userData . variantMaterials . size ) . to . be . eq ( 3 ) ;
144
+ expect ( gltfRoot . children [ 1 ] . userData . variantMaterials . size ) . to . be . eq ( 3 ) ;
145
+ } ) ;
146
+ } ) ;
147
+
148
+ suite ( 'with a loaded model containing a mesh with multiple primitives' , ( ) => {
149
+ setup ( async ( ) => {
150
+ element . src = MESH_PRIMITIVES_GLB_PATH ;
151
+
152
+ await waitForEvent ( element , 'load' ) ;
153
+ await rafPasses ( ) ;
154
+ } ) ;
155
+
156
+ test ( 'has variants' , ( ) => {
157
+ expect ( element [ $scene ] . currentGLTF ! . userData . variants . length )
158
+ . to . be . eq ( 2 ) ;
159
+ const gltfRoot = getGLTFRoot ( element [ $scene ] ) ;
160
+ expect ( gltfRoot . children [ 0 ] . children [ 0 ] . userData . variantMaterials . size ) . to . be . eq ( 2 ) ;
161
+ expect ( gltfRoot . children [ 0 ] . children [ 1 ] . userData . variantMaterials . size ) . to . be . eq ( 2 ) ;
162
+ expect ( gltfRoot . children [ 0 ] . children [ 2 ] . userData . variantMaterials . size ) . to . be . eq ( 2 ) ;
163
+ } ) ;
164
+
165
+ test ( `Setting variantName to null results in primitive
166
+ reverting to default/initial material` , async ( ) => {
167
+ let primitiveNode : PrimitiveNode | null = null
168
+ // Finds the first primitive with material 0 assigned.
169
+ for ( const primitive of element . model ! [ $primitivesList ] ) {
170
+ if ( primitive . variantInfo != null &&
171
+ primitive [ $initialMaterialIdx ] == 0 ) {
172
+ primitiveNode = primitive ;
173
+ return ;
174
+ }
175
+ }
176
+
177
+ expect ( primitiveNode ) . to . not . be . null ;
178
+
179
+ // Switches to a new variant.
180
+ element . variantName = 'Inverse' ;
181
+ await waitForEvent ( element , 'variant-applied' ) ;
182
+ expect ( ( primitiveNode ! . mesh . material as MeshStandardMaterial ) . name )
183
+ . equal ( 'STEEL RED X' ) ;
184
+
185
+ // Switches to null variant.
186
+ element . variantName = null ;
187
+ await waitForEvent ( element , 'variant-applied' ) ;
188
+ expect ( ( primitiveNode ! . mesh . material as MeshStandardMaterial ) . name )
189
+ . equal ( 'STEEL METALLIC' ) ;
190
+ } ) ;
191
+
192
+ test ( 'exports and re-imports the model with variants' , async ( ) => {
193
+ const exported = await element . exportScene ( { binary : true } ) ;
194
+ const url = URL . createObjectURL ( exported ) ;
195
+ element . src = url ;
196
+ await waitForEvent ( element , 'load' ) ;
197
+ await rafPasses ( ) ;
198
+
199
+ expect ( element [ $scene ] . currentGLTF ! . userData . variants . length )
200
+ . to . be . eq ( 2 ) ;
201
+
202
+ const gltfRoot = getGLTFRoot ( element [ $scene ] , true ) ;
203
+ expect ( gltfRoot . children [ 0 ] . children [ 0 ] . userData . variantMaterials . size ) . to . be . eq ( 2 ) ;
204
+ expect ( gltfRoot . children [ 0 ] . children [ 1 ] . userData . variantMaterials . size ) . to . be . eq ( 2 ) ;
205
+ expect ( gltfRoot . children [ 0 ] . children [ 2 ] . userData . variantMaterials . size ) . to . be . eq ( 2 ) ;
140
206
} ) ;
141
207
} ) ;
142
208
0 commit comments