4
4
*/
5
5
6
6
import * as THREE from "three" ;
7
+ import { GLTFParser } from "three/examples/jsm/loaders/GLTFLoader" ;
7
8
8
- import { getAttributeScale , getSkinnedVertex , parseAsBboxRatio } from "../utils" ;
9
+ import { getAttributeScale , getSkinnedVertex , parseAsBboxRatio , isString } from "../utils" ;
10
+ import { VARIANT_EXTENSION } from "../const/internal" ;
9
11
10
12
import { Annotation } from "../annotation" ;
11
13
import { AUTO } from "../const/external" ;
@@ -16,10 +18,12 @@ import { AUTO } from "../const/external";
16
18
class Model {
17
19
private _src : string ;
18
20
private _scene : THREE . Group ;
21
+ private _parser : GLTFParser | null ;
19
22
private _bbox : THREE . Box3 ;
20
23
private _center : THREE . Vector3 ;
21
24
private _animations : THREE . AnimationClip [ ] ;
22
25
private _annotations : Annotation [ ] ;
26
+ private _variants : Array < { name : string } > ;
23
27
private _fixSkinnedBbox : boolean ;
24
28
25
29
/**
@@ -96,17 +100,21 @@ class Model {
96
100
src,
97
101
scenes,
98
102
center = AUTO ,
103
+ parser = null ,
99
104
animations = [ ] ,
100
105
annotations = [ ] ,
106
+ variants = [ ] ,
101
107
fixSkinnedBbox = false ,
102
108
castShadow = true ,
103
109
receiveShadow = false
104
110
} : {
105
111
src : string ;
106
112
scenes : THREE . Object3D [ ] ;
107
113
center ?: typeof AUTO | Array < number | string > ;
114
+ parser ?: GLTFParser | null ,
108
115
animations ?: THREE . AnimationClip [ ] ;
109
116
annotations ?: Annotation [ ] ;
117
+ variants ?: Array < { name : string } > ;
110
118
fixSkinnedBbox ?: boolean ;
111
119
castShadow ?: boolean ;
112
120
receiveShadow ?: boolean ;
@@ -116,9 +124,11 @@ class Model {
116
124
const scene = new THREE . Group ( ) ;
117
125
scene . add ( ...scenes ) ;
118
126
127
+ this . _scene = scene ;
128
+ this . _parser = parser ;
119
129
this . _animations = animations ;
120
130
this . _annotations = annotations ;
121
- this . _scene = scene ;
131
+ this . _variants = variants ;
122
132
const bbox = this . _getInitialBbox ( fixSkinnedBbox ) ;
123
133
124
134
// Move to position where bbox.min.y = 0
@@ -136,6 +146,52 @@ class Model {
136
146
this . receiveShadow = receiveShadow ;
137
147
}
138
148
149
+ public async selectVariant ( variant : number | string | null ) {
150
+ const variants = this . _variants ;
151
+ const parser = this . _parser ;
152
+
153
+ if ( variants . length <= 0 || ! parser ) return ;
154
+
155
+ let variantIndex = 0 ;
156
+ if ( variant != null ) {
157
+ if ( isString ( variant ) ) {
158
+ variantIndex = variants . findIndex ( ( { name } ) => name === variant ) ;
159
+ } else {
160
+ variantIndex = variant
161
+ }
162
+ }
163
+
164
+ const scene = this . _scene ;
165
+ const matLoadPromises : Promise < any > [ ] = [ ] ;
166
+
167
+ scene . traverse ( async ( obj : THREE . Mesh ) => {
168
+ if ( ! obj . isMesh || ! obj . userData . gltfExtensions ) return ;
169
+
170
+ const meshVariantDef = obj . userData . gltfExtensions [ VARIANT_EXTENSION ] ;
171
+
172
+ if ( ! meshVariantDef ) return ;
173
+
174
+ if ( ! obj . userData . originalMaterial ) {
175
+ obj . userData . originalMaterial = obj . material ;
176
+ }
177
+
178
+ const mapping = meshVariantDef . mappings
179
+ . find ( mapping => mapping . variants . includes ( variantIndex ) ) ;
180
+
181
+ if ( mapping ) {
182
+ const loadMat = parser . getDependency ( "material" , mapping . material ) ;
183
+ matLoadPromises . push ( loadMat ) ;
184
+
185
+ obj . material = await loadMat ;
186
+ parser . assignFinalMaterial ( obj ) ;
187
+ } else {
188
+ obj . material = obj . userData . originalMaterial ;
189
+ }
190
+ } ) ;
191
+
192
+ return Promise . all ( matLoadPromises ) ;
193
+ }
194
+
139
195
/**
140
196
* Executes a user-supplied "reducer" callback function on each vertex of the model, in order, passing in the return value from the calculation on the preceding element.
141
197
*/
0 commit comments