elation-engine 0.9.113 → 0.9.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css/systems/render.css +5 -1
- package/package.json +1 -1
- package/scripts/assets.js +103 -20
- package/scripts/assetworker.js +18 -1
- package/scripts/external/holoplay.js +1494 -0
- package/scripts/external/octree.js +0 -0
- package/scripts/external/three/CSS3DRenderer.js +46 -43
- package/scripts/external/three/CubemapToEquirectangular.js +1 -1
- package/scripts/external/three/three-extras.js +1553 -392
- package/scripts/external/three/three-icosa.js +2575 -0
- package/scripts/external/three/three-loaders.js +925 -133
- package/scripts/external/three/three-postprocessing.js +3 -3
- package/scripts/external/three/three-r116dev.js +50930 -0
- package/scripts/external/three/three-spotlighttextures.js +50953 -0
- package/scripts/external/three/three-vrm.js +2 -2
- package/scripts/external/three/three-working.js +35968 -0
- package/scripts/external/three/three.js +38532 -24087
- package/scripts/external/three-mesh-bvh.js +5370 -0
- package/scripts/external/three-old/BVHLoader.js +406 -0
- package/scripts/external/three-old/ColladaLoader.js +5519 -0
- package/scripts/external/three-old/ColladaLoader2.js +1694 -0
- package/scripts/external/three-old/CubemapToEquirectangular.js +188 -0
- package/scripts/external/three-old/DDSLoader.js +269 -0
- package/scripts/external/three-old/FBXLoader-mine.js +5063 -0
- package/scripts/external/three-old/FBXLoader.js +5112 -0
- package/scripts/external/three-old/FlyControls.js +295 -0
- package/scripts/external/three-old/GLTF2Loader.js +2950 -0
- package/scripts/external/three-old/GLTFLoader.js +2213 -0
- package/scripts/external/three-old/JSONLoader.js +435 -0
- package/scripts/external/three-old/MTLLoader.js +533 -0
- package/scripts/external/three-old/OBJLoader-experimental.js +874 -0
- package/scripts/external/three-old/OBJLoader-working.js +727 -0
- package/scripts/external/three-old/OBJLoader.js +723 -0
- package/scripts/external/three-old/OBJMTLLoader.js +440 -0
- package/scripts/external/three-old/OrbitControls.js +592 -0
- package/scripts/external/three-old/PLYLoader.js +517 -0
- package/scripts/external/three-old/TransformControls.js +1100 -0
- package/scripts/external/three-old/VRMLLoader.js +1021 -0
- package/scripts/external/three-old/glTFLoader-combined.js +2513 -0
- package/scripts/external/three-old/nodethree.js +44018 -0
- package/scripts/external/three-old/render/BleachBypassShader.js +64 -0
- package/scripts/external/three-old/render/BloomPass.js +116 -0
- package/scripts/external/three-old/render/CSS3DRenderer.js +310 -0
- package/scripts/external/three-old/render/ClearPass.js +44 -0
- package/scripts/external/three-old/render/ConvolutionShader.js +101 -0
- package/scripts/external/three-old/render/CopyShader.js +46 -0
- package/scripts/external/three-old/render/EffectComposer.js +211 -0
- package/scripts/external/three-old/render/FXAAShader.js +88 -0
- package/scripts/external/three-old/render/FilmPass.js +60 -0
- package/scripts/external/three-old/render/FilmShader.js +104 -0
- package/scripts/external/three-old/render/ManualMSAARenderPass.js +168 -0
- package/scripts/external/three-old/render/MaskPass.js +97 -0
- package/scripts/external/three-old/render/OculusRenderPass.js +84 -0
- package/scripts/external/three-old/render/OculusRiftEffect.js +240 -0
- package/scripts/external/three-old/render/PortalRenderPass.js +166 -0
- package/scripts/external/three-old/render/RecordingPass.js +208 -0
- package/scripts/external/three-old/render/RenderPass.js +57 -0
- package/scripts/external/three-old/render/SSAOShader.js +259 -0
- package/scripts/external/three-old/render/SepiaShader.js +54 -0
- package/scripts/external/three-old/render/ShaderPass.js +66 -0
- package/scripts/external/three-old/render/VREffect.js +482 -0
- package/scripts/external/three-old/shimthree.js +23 -0
- package/scripts/external/three-old/stats.js +6 -0
- package/scripts/external/three-old/three-88dev.js +45004 -0
- package/scripts/external/three-old/three-backgroundoptimization.js +44432 -0
- package/scripts/external/three-old/three-updates.js +44735 -0
- package/scripts/external/three-old/three-working.js +44719 -0
- package/scripts/external/three-old/three.js +44431 -0
- package/scripts/external/three-old/threex.rendererstats.js +66 -0
- package/scripts/external/three-old/tween.js +13 -0
- package/scripts/external/webvr-polyfill-new.js +3497 -0
- package/scripts/external/webvr-polyfill-newest.js +3491 -0
- package/scripts/external/webvr-polyfill-old.js +6337 -0
- package/scripts/geometries.js +2 -2
- package/scripts/math.js +6 -6
- package/scripts/systems/admin.js +1 -1
- package/scripts/systems/controls.js +6 -4
- package/scripts/systems/physics.js +10 -10
- package/scripts/systems/render.js +58 -20
- package/scripts/systems/render2.js +38 -0
- package/scripts/things/camera.js +6 -1
- package/scripts/things/generic-trackedvectors.js +1875 -0
- package/scripts/things/generic.js +3 -4
- package/scripts/things/label2d.js +1 -1
- package/scripts/things/leapmotion.js +6 -6
- package/scripts/things/menu.js +1 -1
- package/scripts/things/player-bak.js +638 -0
- package/scripts/things/player.js +28 -10
- package/scripts/things/skysphere.js +1 -1
- package/scripts/things/terrain.js +1 -1
- package/scripts/things/text.js +1 -1
|
@@ -0,0 +1,5063 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Kyle-Larson https://github.com/Kyle-Larson
|
|
3
|
+
* @author Takahiro https://github.com/takahirox
|
|
4
|
+
*
|
|
5
|
+
* Loader loads FBX file and generates Group representing FBX scene.
|
|
6
|
+
* Requires FBX file to be >= 7.0 and in ASCII or to be any version in Binary format.
|
|
7
|
+
*
|
|
8
|
+
* Supports:
|
|
9
|
+
* Mesh Generation (Positional Data)
|
|
10
|
+
* Normal Data (Per Vertex Drawing Instance)
|
|
11
|
+
* UV Data (Per Vertex Drawing Instance)
|
|
12
|
+
* Skinning
|
|
13
|
+
* Animation
|
|
14
|
+
* - Separated Animations based on stacks.
|
|
15
|
+
* - Skeletal & Non-Skeletal Animations
|
|
16
|
+
* NURBS (Open, Closed and Periodic forms)
|
|
17
|
+
*
|
|
18
|
+
* Needs Support:
|
|
19
|
+
* Indexed Buffers
|
|
20
|
+
* PreRotation support.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
( function () {
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generates a loader for loading FBX files from URL and parsing into
|
|
27
|
+
* a THREE.Group.
|
|
28
|
+
* @param {THREE.LoadingManager} manager - Loading Manager for loader to use.
|
|
29
|
+
*/
|
|
30
|
+
THREE.FBXLoader = function ( manager ) {
|
|
31
|
+
|
|
32
|
+
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
|
|
33
|
+
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
Object.assign( THREE.FBXLoader.prototype, {
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Loads an ASCII/Binary FBX file from URL and parses into a THREE.Group.
|
|
40
|
+
* THREE.Group will have an animations property of AnimationClips
|
|
41
|
+
* of the different animations exported with the FBX.
|
|
42
|
+
* @param {string} url - URL of the FBX file.
|
|
43
|
+
* @param {function(THREE.Group):void} onLoad - Callback for when FBX file is loaded and parsed.
|
|
44
|
+
* @param {function(ProgressEvent):void} onProgress - Callback fired periodically when file is being retrieved from server.
|
|
45
|
+
* @param {function(Event):void} onError - Callback fired when error occurs (Currently only with retrieving file, not with parsing errors).
|
|
46
|
+
*/
|
|
47
|
+
load: function ( url, onLoad, onProgress, onError ) {
|
|
48
|
+
|
|
49
|
+
var self = this;
|
|
50
|
+
|
|
51
|
+
var resourceDirectory = url.split( /[\\\/]/ );
|
|
52
|
+
resourceDirectory.pop();
|
|
53
|
+
resourceDirectory = resourceDirectory.join( '/' ) + '/';
|
|
54
|
+
|
|
55
|
+
var loader = new THREE.FileLoader( this.manager );
|
|
56
|
+
loader.setResponseType( 'arraybuffer' );
|
|
57
|
+
loader.load( url, function ( buffer ) {
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
|
|
61
|
+
var scene = self.parse( buffer, resourceDirectory );
|
|
62
|
+
|
|
63
|
+
onLoad( scene );
|
|
64
|
+
|
|
65
|
+
} catch ( error ) {
|
|
66
|
+
|
|
67
|
+
setTimeout( function () {
|
|
68
|
+
|
|
69
|
+
if ( onError ) onError( error );
|
|
70
|
+
|
|
71
|
+
self.manager.itemError( url );
|
|
72
|
+
|
|
73
|
+
}, 0 );
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
}, onProgress, onError );
|
|
78
|
+
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Parses an ASCII/Binary FBX file and returns a THREE.Group.
|
|
83
|
+
* THREE.Group will have an animations property of AnimationClips
|
|
84
|
+
* of the different animations within the FBX file.
|
|
85
|
+
* @param {ArrayBuffer} FBXBuffer - Contents of FBX file to parse.
|
|
86
|
+
* @param {string} resourceDirectory - Directory to load external assets (e.g. textures ) from.
|
|
87
|
+
* @returns {THREE.Group}
|
|
88
|
+
*/
|
|
89
|
+
parse: function ( FBXBuffer, resourceDirectory ) {
|
|
90
|
+
|
|
91
|
+
var FBXTree;
|
|
92
|
+
|
|
93
|
+
if ( isFbxFormatBinary( FBXBuffer ) ) {
|
|
94
|
+
|
|
95
|
+
FBXTree = new BinaryParser().parse( FBXBuffer );
|
|
96
|
+
|
|
97
|
+
} else {
|
|
98
|
+
|
|
99
|
+
var FBXText = convertArrayBufferToString( FBXBuffer );
|
|
100
|
+
|
|
101
|
+
if ( ! isFbxFormatASCII( FBXText ) ) {
|
|
102
|
+
|
|
103
|
+
throw new Error( 'FBXLoader: Unknown format.' );
|
|
104
|
+
//self.manager.itemError( url );
|
|
105
|
+
return;
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if ( getFbxVersion( FBXText ) < 7000 ) {
|
|
110
|
+
|
|
111
|
+
throw new Error( 'FBXLoader: FBX version not supported for file at ' + '(unknown)' + ', FileVersion: ' + getFbxVersion( FBXText ) );
|
|
112
|
+
//self.manager.itemError( url );
|
|
113
|
+
return;
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
FBXTree = new TextParser().parse( FBXText );
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log('FBX tree', FBXTree);
|
|
122
|
+
// console.log( FBXTree );
|
|
123
|
+
|
|
124
|
+
var connections = parseConnections( FBXTree );
|
|
125
|
+
var images = parseImages( FBXTree );
|
|
126
|
+
var textures = parseTextures( FBXTree, new THREE.TextureLoader( this.manager ).setPath( resourceDirectory ), images, connections );
|
|
127
|
+
var materials = parseMaterials( FBXTree, textures, connections );
|
|
128
|
+
var deformers = parseDeformers( FBXTree, connections );
|
|
129
|
+
var geometryMap = parseGeometries( FBXTree, connections, deformers );
|
|
130
|
+
var sceneGraph = parseScene( FBXTree, connections, deformers, geometryMap, materials );
|
|
131
|
+
|
|
132
|
+
return sceneGraph;
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
} );
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Parses map of relationships between objects.
|
|
140
|
+
* @param {{Connections: { properties: { connections: [number, number, string][]}}}} FBXTree
|
|
141
|
+
* @returns {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>}
|
|
142
|
+
*/
|
|
143
|
+
function parseConnections( FBXTree ) {
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @type {Map<number, { parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>}
|
|
147
|
+
*/
|
|
148
|
+
var connectionMap = new Map();
|
|
149
|
+
|
|
150
|
+
if ( 'Connections' in FBXTree ) {
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @type {[number, number, string][]}
|
|
154
|
+
*/
|
|
155
|
+
var connectionArray = FBXTree.Connections.properties.connections;
|
|
156
|
+
for ( var connectionArrayIndex = 0, connectionArrayLength = connectionArray.length; connectionArrayIndex < connectionArrayLength; ++ connectionArrayIndex ) {
|
|
157
|
+
|
|
158
|
+
var connection = connectionArray[ connectionArrayIndex ];
|
|
159
|
+
|
|
160
|
+
if ( ! connectionMap.has( connection[ 0 ] ) ) {
|
|
161
|
+
|
|
162
|
+
connectionMap.set( connection[ 0 ], {
|
|
163
|
+
parents: [],
|
|
164
|
+
children: []
|
|
165
|
+
} );
|
|
166
|
+
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
var parentRelationship = { ID: connection[ 1 ], relationship: connection[ 2 ] };
|
|
170
|
+
connectionMap.get( connection[ 0 ] ).parents.push( parentRelationship );
|
|
171
|
+
|
|
172
|
+
if ( ! connectionMap.has( connection[ 1 ] ) ) {
|
|
173
|
+
|
|
174
|
+
connectionMap.set( connection[ 1 ], {
|
|
175
|
+
parents: [],
|
|
176
|
+
children: []
|
|
177
|
+
} );
|
|
178
|
+
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
var childRelationship = { ID: connection[ 0 ], relationship: connection[ 2 ] };
|
|
182
|
+
connectionMap.get( connection[ 1 ] ).children.push( childRelationship );
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return connectionMap;
|
|
189
|
+
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Parses map of images referenced in FBXTree.
|
|
194
|
+
* @param {{Objects: {subNodes: {Texture: Object.<string, FBXTextureNode>}}}} FBXTree
|
|
195
|
+
* @returns {Map<number, string(image blob URL)>}
|
|
196
|
+
*/
|
|
197
|
+
function parseImages( FBXTree ) {
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @type {Map<number, string(image blob URL)>}
|
|
201
|
+
*/
|
|
202
|
+
var imageMap = new Map();
|
|
203
|
+
|
|
204
|
+
if ( 'Video' in FBXTree.Objects.subNodes ) {
|
|
205
|
+
|
|
206
|
+
var videoNodes = FBXTree.Objects.subNodes.Video;
|
|
207
|
+
|
|
208
|
+
for ( var nodeID in videoNodes ) {
|
|
209
|
+
|
|
210
|
+
var videoNode = videoNodes[ nodeID ];
|
|
211
|
+
|
|
212
|
+
// raw image data is in videoNode.properties.Content
|
|
213
|
+
if ( 'Content' in videoNode.properties ) {
|
|
214
|
+
|
|
215
|
+
var image = parseImage( videoNodes[ nodeID ] );
|
|
216
|
+
imageMap.set( parseInt( nodeID ), image );
|
|
217
|
+
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return imageMap;
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @param {videoNode} videoNode - Node to get texture image information from.
|
|
230
|
+
* @returns {string} - image blob URL
|
|
231
|
+
*/
|
|
232
|
+
function parseImage( videoNode ) {
|
|
233
|
+
|
|
234
|
+
var buffer = videoNode.properties.Content;
|
|
235
|
+
var array = new Uint8Array( buffer );
|
|
236
|
+
var fileName = videoNode.properties.RelativeFilename || videoNode.properties.Filename;
|
|
237
|
+
var extension = fileName.slice( fileName.lastIndexOf( '.' ) + 1 ).toLowerCase();
|
|
238
|
+
|
|
239
|
+
var type;
|
|
240
|
+
|
|
241
|
+
switch ( extension ) {
|
|
242
|
+
|
|
243
|
+
case 'bmp':
|
|
244
|
+
|
|
245
|
+
type = 'image/bmp';
|
|
246
|
+
break;
|
|
247
|
+
|
|
248
|
+
case 'jpg':
|
|
249
|
+
|
|
250
|
+
type = 'image/jpeg';
|
|
251
|
+
break;
|
|
252
|
+
|
|
253
|
+
case 'png':
|
|
254
|
+
|
|
255
|
+
type = 'image/png';
|
|
256
|
+
break;
|
|
257
|
+
|
|
258
|
+
case 'tif':
|
|
259
|
+
|
|
260
|
+
type = 'image/tiff';
|
|
261
|
+
break;
|
|
262
|
+
|
|
263
|
+
default:
|
|
264
|
+
|
|
265
|
+
console.warn( 'FBXLoader: No support image type ' + extension );
|
|
266
|
+
return;
|
|
267
|
+
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return self.URL.createObjectURL( new Blob( [ array ], { type: type } ) );
|
|
271
|
+
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Parses map of textures referenced in FBXTree.
|
|
276
|
+
* @param {{Objects: {subNodes: {Texture: Object.<string, FBXTextureNode>}}}} FBXTree
|
|
277
|
+
* @param {THREE.TextureLoader} loader
|
|
278
|
+
* @param {Map<number, string(image blob URL)>} imageMap
|
|
279
|
+
* @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
|
|
280
|
+
* @returns {Map<number, THREE.Texture>}
|
|
281
|
+
*/
|
|
282
|
+
function parseTextures( FBXTree, loader, imageMap, connections ) {
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @type {Map<number, THREE.Texture>}
|
|
286
|
+
*/
|
|
287
|
+
var textureMap = new Map();
|
|
288
|
+
|
|
289
|
+
if ( 'Texture' in FBXTree.Objects.subNodes ) {
|
|
290
|
+
|
|
291
|
+
var textureNodes = FBXTree.Objects.subNodes.Texture;
|
|
292
|
+
for ( var nodeID in textureNodes ) {
|
|
293
|
+
|
|
294
|
+
var texture = parseTexture( textureNodes[ nodeID ], loader, imageMap, connections );
|
|
295
|
+
textureMap.set( parseInt( nodeID ), texture );
|
|
296
|
+
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return textureMap;
|
|
302
|
+
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @param {textureNode} textureNode - Node to get texture information from.
|
|
307
|
+
* @param {THREE.TextureLoader} loader
|
|
308
|
+
* @param {Map<number, string(image blob URL)>} imageMap
|
|
309
|
+
* @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
|
|
310
|
+
* @returns {THREE.Texture}
|
|
311
|
+
*/
|
|
312
|
+
function parseTexture( textureNode, loader, imageMap, connections ) {
|
|
313
|
+
|
|
314
|
+
var FBX_ID = textureNode.id;
|
|
315
|
+
|
|
316
|
+
var name = textureNode.name;
|
|
317
|
+
|
|
318
|
+
var fileName;
|
|
319
|
+
|
|
320
|
+
var filePath = textureNode.properties.FileName;
|
|
321
|
+
var relativeFilePath = textureNode.properties.RelativeFilename;
|
|
322
|
+
|
|
323
|
+
var children = connections.get( FBX_ID ).children;
|
|
324
|
+
|
|
325
|
+
if ( children !== undefined && children.length > 0 && imageMap.has( children[ 0 ].ID ) ) {
|
|
326
|
+
|
|
327
|
+
fileName = imageMap.get( children[ 0 ].ID );
|
|
328
|
+
|
|
329
|
+
} else if ( relativeFilePath !== undefined && relativeFilePath[ 0 ] !== '/' &&
|
|
330
|
+
relativeFilePath.match( /^[a-zA-Z]:/ ) === null ) {
|
|
331
|
+
|
|
332
|
+
// use textureNode.properties.RelativeFilename
|
|
333
|
+
// if it exists and it doesn't seem an absolute path
|
|
334
|
+
|
|
335
|
+
fileName = relativeFilePath;
|
|
336
|
+
|
|
337
|
+
} else {
|
|
338
|
+
|
|
339
|
+
var split = filePath.split( /[\\\/]/ );
|
|
340
|
+
|
|
341
|
+
if ( split.length > 0 ) {
|
|
342
|
+
|
|
343
|
+
fileName = split[ split.length - 1 ];
|
|
344
|
+
|
|
345
|
+
} else {
|
|
346
|
+
|
|
347
|
+
fileName = filePath;
|
|
348
|
+
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
var currentPath = loader.path;
|
|
354
|
+
|
|
355
|
+
if ( fileName.indexOf( 'blob:' ) === 0 ) {
|
|
356
|
+
|
|
357
|
+
loader.setPath( undefined );
|
|
358
|
+
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @type {THREE.Texture}
|
|
363
|
+
*/
|
|
364
|
+
var texture = loader.load( fileName );
|
|
365
|
+
texture.name = name;
|
|
366
|
+
texture.FBX_ID = FBX_ID;
|
|
367
|
+
|
|
368
|
+
loader.setPath( currentPath );
|
|
369
|
+
|
|
370
|
+
return texture;
|
|
371
|
+
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Parses map of Material information.
|
|
376
|
+
* @param {{Objects: {subNodes: {Material: Object.<number, FBXMaterialNode>}}}} FBXTree
|
|
377
|
+
* @param {Map<number, THREE.Texture>} textureMap
|
|
378
|
+
* @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
|
|
379
|
+
* @returns {Map<number, THREE.Material>}
|
|
380
|
+
*/
|
|
381
|
+
function parseMaterials( FBXTree, textureMap, connections ) {
|
|
382
|
+
|
|
383
|
+
var materialMap = new Map();
|
|
384
|
+
|
|
385
|
+
if ( 'Material' in FBXTree.Objects.subNodes ) {
|
|
386
|
+
|
|
387
|
+
var materialNodes = FBXTree.Objects.subNodes.Material;
|
|
388
|
+
for ( var nodeID in materialNodes ) {
|
|
389
|
+
|
|
390
|
+
var material = parseMaterial( materialNodes[ nodeID ], textureMap, connections );
|
|
391
|
+
materialMap.set( parseInt( nodeID ), material );
|
|
392
|
+
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return materialMap;
|
|
398
|
+
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Takes information from Material node and returns a generated THREE.Material
|
|
403
|
+
* @param {FBXMaterialNode} materialNode
|
|
404
|
+
* @param {Map<number, THREE.Texture>} textureMap
|
|
405
|
+
* @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
|
|
406
|
+
* @returns {THREE.Material}
|
|
407
|
+
*/
|
|
408
|
+
function parseMaterial( materialNode, textureMap, connections ) {
|
|
409
|
+
|
|
410
|
+
var FBX_ID = materialNode.id;
|
|
411
|
+
var name = materialNode.attrName;
|
|
412
|
+
var type = materialNode.properties.ShadingModel;
|
|
413
|
+
|
|
414
|
+
//Case where FBXs wrap shading model in property object.
|
|
415
|
+
if ( typeof type === 'object' ) {
|
|
416
|
+
|
|
417
|
+
type = type.value;
|
|
418
|
+
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
var children = connections.get( FBX_ID ).children;
|
|
422
|
+
|
|
423
|
+
var parameters = parseParameters( materialNode.properties, textureMap, children );
|
|
424
|
+
|
|
425
|
+
var material;
|
|
426
|
+
|
|
427
|
+
switch ( type ) {
|
|
428
|
+
|
|
429
|
+
case 'phong':
|
|
430
|
+
material = new THREE.MeshPhongMaterial();
|
|
431
|
+
break;
|
|
432
|
+
case 'lambert':
|
|
433
|
+
material = new THREE.MeshLambertMaterial();
|
|
434
|
+
break;
|
|
435
|
+
default:
|
|
436
|
+
console.warn( 'No implementation given for material type ' + type + ' in FBXLoader.js. Defaulting to basic material' );
|
|
437
|
+
material = new THREE.MeshBasicMaterial( { color: 0x3300ff } );
|
|
438
|
+
break;
|
|
439
|
+
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
material.setValues( parameters );
|
|
443
|
+
material.name = name;
|
|
444
|
+
|
|
445
|
+
return material;
|
|
446
|
+
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* @typedef {{Diffuse: FBXVector3, Specular: FBXVector3, Shininess: FBXValue, Emissive: FBXVector3, EmissiveFactor: FBXValue, Opacity: FBXValue}} FBXMaterialProperties
|
|
451
|
+
*/
|
|
452
|
+
/**
|
|
453
|
+
* @typedef {{color: THREE.Color=, specular: THREE.Color=, shininess: number=, emissive: THREE.Color=, emissiveIntensity: number=, opacity: number=, transparent: boolean=, map: THREE.Texture=}} THREEMaterialParameterPack
|
|
454
|
+
*/
|
|
455
|
+
/**
|
|
456
|
+
* @param {FBXMaterialProperties} properties
|
|
457
|
+
* @param {Map<number, THREE.Texture>} textureMap
|
|
458
|
+
* @param {{ID: number, relationship: string}[]} childrenRelationships
|
|
459
|
+
* @returns {THREEMaterialParameterPack}
|
|
460
|
+
*/
|
|
461
|
+
function parseParameters( properties, textureMap, childrenRelationships ) {
|
|
462
|
+
|
|
463
|
+
var parameters = {};
|
|
464
|
+
|
|
465
|
+
if ( properties.Diffuse ) {
|
|
466
|
+
|
|
467
|
+
parameters.color = parseColor( properties.Diffuse );
|
|
468
|
+
|
|
469
|
+
}
|
|
470
|
+
if ( properties.Specular ) {
|
|
471
|
+
|
|
472
|
+
parameters.specular = parseColor( properties.Specular );
|
|
473
|
+
|
|
474
|
+
}
|
|
475
|
+
if ( properties.Shininess ) {
|
|
476
|
+
|
|
477
|
+
parameters.shininess = properties.Shininess.value;
|
|
478
|
+
|
|
479
|
+
}
|
|
480
|
+
if ( properties.Emissive ) {
|
|
481
|
+
|
|
482
|
+
parameters.emissive = parseColor( properties.Emissive );
|
|
483
|
+
|
|
484
|
+
}
|
|
485
|
+
if ( properties.EmissiveFactor ) {
|
|
486
|
+
|
|
487
|
+
parameters.emissiveIntensity = properties.EmissiveFactor.value;
|
|
488
|
+
|
|
489
|
+
}
|
|
490
|
+
if ( properties.Opacity ) {
|
|
491
|
+
|
|
492
|
+
parameters.opacity = properties.Opacity.value;
|
|
493
|
+
|
|
494
|
+
}
|
|
495
|
+
if ( parameters.opacity < 1.0 ) {
|
|
496
|
+
|
|
497
|
+
parameters.transparent = true;
|
|
498
|
+
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
for ( var childrenRelationshipsIndex = 0, childrenRelationshipsLength = childrenRelationships.length; childrenRelationshipsIndex < childrenRelationshipsLength; ++ childrenRelationshipsIndex ) {
|
|
502
|
+
|
|
503
|
+
var relationship = childrenRelationships[ childrenRelationshipsIndex ];
|
|
504
|
+
|
|
505
|
+
var type = relationship.relationship;
|
|
506
|
+
|
|
507
|
+
switch ( type ) {
|
|
508
|
+
|
|
509
|
+
case "DiffuseColor":
|
|
510
|
+
case " \"DiffuseColor":
|
|
511
|
+
parameters.map = textureMap.get( relationship.ID );
|
|
512
|
+
break;
|
|
513
|
+
|
|
514
|
+
case "Bump":
|
|
515
|
+
case " \"Bump":
|
|
516
|
+
parameters.bumpMap = textureMap.get( relationship.ID );
|
|
517
|
+
break;
|
|
518
|
+
|
|
519
|
+
case "NormalMap":
|
|
520
|
+
case " \"NormalMap":
|
|
521
|
+
parameters.normalMap = textureMap.get( relationship.ID );
|
|
522
|
+
break;
|
|
523
|
+
|
|
524
|
+
case " \"AmbientColor":
|
|
525
|
+
case " \"EmissiveColor":
|
|
526
|
+
case "AmbientColor":
|
|
527
|
+
case "EmissiveColor":
|
|
528
|
+
default:
|
|
529
|
+
console.warn( 'Unknown texture application of type ' + type + ', skipping texture' );
|
|
530
|
+
break;
|
|
531
|
+
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return parameters;
|
|
537
|
+
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Generates map of Skeleton-like objects for use later when generating and binding skeletons.
|
|
542
|
+
* @param {{Objects: {subNodes: {Deformer: Object.<number, FBXSubDeformerNode>}}}} FBXTree
|
|
543
|
+
* @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
|
|
544
|
+
* @returns {Map<number, {map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}>}
|
|
545
|
+
*/
|
|
546
|
+
function parseDeformers( FBXTree, connections ) {
|
|
547
|
+
|
|
548
|
+
var deformers = {};
|
|
549
|
+
|
|
550
|
+
if ( 'Deformer' in FBXTree.Objects.subNodes ) {
|
|
551
|
+
|
|
552
|
+
var DeformerNodes = FBXTree.Objects.subNodes.Deformer;
|
|
553
|
+
|
|
554
|
+
for ( var nodeID in DeformerNodes ) {
|
|
555
|
+
|
|
556
|
+
var deformerNode = DeformerNodes[ nodeID ];
|
|
557
|
+
|
|
558
|
+
if ( deformerNode.attrType === 'Skin' ) {
|
|
559
|
+
|
|
560
|
+
var conns = connections.get( parseInt( nodeID ) );
|
|
561
|
+
var skeleton = parseSkeleton( conns, DeformerNodes );
|
|
562
|
+
skeleton.FBX_ID = parseInt( nodeID );
|
|
563
|
+
|
|
564
|
+
deformers[ nodeID ] = skeleton;
|
|
565
|
+
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return deformers;
|
|
573
|
+
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Generates a "Skeleton Representation" of FBX nodes based on an FBX Skin Deformer's connections and an object containing SubDeformer nodes.
|
|
578
|
+
* @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} connections
|
|
579
|
+
* @param {Object.<number, FBXSubDeformerNode>} DeformerNodes
|
|
580
|
+
* @returns {{map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}}
|
|
581
|
+
*/
|
|
582
|
+
function parseSkeleton( connections, DeformerNodes ) {
|
|
583
|
+
|
|
584
|
+
var subDeformers = {};
|
|
585
|
+
var children = connections.children;
|
|
586
|
+
|
|
587
|
+
for ( var i = 0, l = children.length; i < l; ++ i ) {
|
|
588
|
+
|
|
589
|
+
var child = children[ i ];
|
|
590
|
+
|
|
591
|
+
var subDeformerNode = DeformerNodes[ child.ID ];
|
|
592
|
+
|
|
593
|
+
var subDeformer = {
|
|
594
|
+
FBX_ID: child.ID,
|
|
595
|
+
index: i,
|
|
596
|
+
indices: [],
|
|
597
|
+
weights: [],
|
|
598
|
+
transform: parseMatrixArray( subDeformerNode.subNodes.Transform.properties.a ),
|
|
599
|
+
transformLink: parseMatrixArray( subDeformerNode.subNodes.TransformLink.properties.a ),
|
|
600
|
+
linkMode: subDeformerNode.properties.Mode
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
if ( 'Indexes' in subDeformerNode.subNodes ) {
|
|
604
|
+
|
|
605
|
+
subDeformer.indices = parseIntArray( subDeformerNode.subNodes.Indexes.properties.a );
|
|
606
|
+
subDeformer.weights = parseFloatArray( subDeformerNode.subNodes.Weights.properties.a );
|
|
607
|
+
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
subDeformers[ child.ID ] = subDeformer;
|
|
611
|
+
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return {
|
|
615
|
+
map: subDeformers,
|
|
616
|
+
bones: []
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Generates Buffer geometries from geometry information in FBXTree, and generates map of THREE.BufferGeometries
|
|
623
|
+
* @param {{Objects: {subNodes: {Geometry: Object.<number, FBXGeometryNode}}}} FBXTree
|
|
624
|
+
* @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
|
|
625
|
+
* @param {Map<number, {map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}>} deformers
|
|
626
|
+
* @returns {Map<number, THREE.BufferGeometry>}
|
|
627
|
+
*/
|
|
628
|
+
function parseGeometries( FBXTree, connections, deformers ) {
|
|
629
|
+
|
|
630
|
+
var geometryMap = new Map();
|
|
631
|
+
|
|
632
|
+
if ( 'Geometry' in FBXTree.Objects.subNodes ) {
|
|
633
|
+
|
|
634
|
+
var geometryNodes = FBXTree.Objects.subNodes.Geometry;
|
|
635
|
+
|
|
636
|
+
for ( var nodeID in geometryNodes ) {
|
|
637
|
+
|
|
638
|
+
var relationships = connections.get( parseInt( nodeID ) );
|
|
639
|
+
var geo = parseGeometry( geometryNodes[ nodeID ], relationships, deformers );
|
|
640
|
+
geometryMap.set( parseInt( nodeID ), geo );
|
|
641
|
+
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
return geometryMap;
|
|
647
|
+
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Generates BufferGeometry from FBXGeometryNode.
|
|
652
|
+
* @param {FBXGeometryNode} geometryNode
|
|
653
|
+
* @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} relationships
|
|
654
|
+
* @param {Map<number, {map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[]}>} deformers
|
|
655
|
+
* @returns {THREE.BufferGeometry}
|
|
656
|
+
*/
|
|
657
|
+
function parseGeometry( geometryNode, relationships, deformers ) {
|
|
658
|
+
|
|
659
|
+
switch ( geometryNode.attrType ) {
|
|
660
|
+
|
|
661
|
+
case 'Mesh':
|
|
662
|
+
return parseMeshGeometry( geometryNode, relationships, deformers );
|
|
663
|
+
break;
|
|
664
|
+
|
|
665
|
+
case 'NurbsCurve':
|
|
666
|
+
return parseNurbsGeometry( geometryNode );
|
|
667
|
+
break;
|
|
668
|
+
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Specialty function for parsing Mesh based Geometry Nodes.
|
|
675
|
+
* @param {FBXGeometryNode} geometryNode
|
|
676
|
+
* @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} relationships - Object representing relationships between specific geometry node and other nodes.
|
|
677
|
+
* @param {Map<number, {map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[]}>} deformers - Map object of deformers and subDeformers by ID.
|
|
678
|
+
* @returns {THREE.BufferGeometry}
|
|
679
|
+
*/
|
|
680
|
+
function parseMeshGeometry( geometryNode, relationships, deformers ) {
|
|
681
|
+
|
|
682
|
+
for ( var i = 0; i < relationships.children.length; ++ i ) {
|
|
683
|
+
|
|
684
|
+
var deformer = deformers[ relationships.children[ i ].ID ];
|
|
685
|
+
if ( deformer !== undefined ) break;
|
|
686
|
+
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return genGeometry( geometryNode, deformer );
|
|
690
|
+
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* @param {{map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[]}} deformer - Skeleton representation for geometry instance.
|
|
695
|
+
* @returns {THREE.BufferGeometry}
|
|
696
|
+
*/
|
|
697
|
+
function genGeometry( geometryNode, deformer ) {
|
|
698
|
+
|
|
699
|
+
var geometry = new Geometry();
|
|
700
|
+
|
|
701
|
+
var subNodes = geometryNode.subNodes;
|
|
702
|
+
|
|
703
|
+
// First, each index is going to be its own vertex.
|
|
704
|
+
|
|
705
|
+
var vertexBuffer = parseFloatArray( subNodes.Vertices.properties.a );
|
|
706
|
+
var indexBuffer = parseIntArray( subNodes.PolygonVertexIndex.properties.a );
|
|
707
|
+
|
|
708
|
+
if ( subNodes.LayerElementNormal ) {
|
|
709
|
+
|
|
710
|
+
var normalInfo = getNormals( subNodes.LayerElementNormal[ 0 ] );
|
|
711
|
+
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
if ( subNodes.LayerElementUV ) {
|
|
715
|
+
|
|
716
|
+
var uvInfo = getUVs( subNodes.LayerElementUV[ 0 ] );
|
|
717
|
+
|
|
718
|
+
var uv2Info = false;
|
|
719
|
+
|
|
720
|
+
if ( subNodes.LayerElementUV[ 1 ] ) {
|
|
721
|
+
|
|
722
|
+
uv2Info = getUVs( subNodes.LayerElementUV[ 1 ] );
|
|
723
|
+
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
if ( subNodes.LayerElementColor ) {
|
|
729
|
+
|
|
730
|
+
var colorInfo = getColors( subNodes.LayerElementColor[ 0 ] );
|
|
731
|
+
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if ( subNodes.LayerElementMaterial ) {
|
|
735
|
+
|
|
736
|
+
var materialInfo = getMaterials( subNodes.LayerElementMaterial[ 0 ] );
|
|
737
|
+
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
var faceVertexBuffer = [];
|
|
741
|
+
var polygonIndex = 0;
|
|
742
|
+
|
|
743
|
+
for ( var polygonVertexIndex = 0; polygonVertexIndex < indexBuffer.length; polygonVertexIndex ++ ) {
|
|
744
|
+
|
|
745
|
+
var vertexIndex = indexBuffer[ polygonVertexIndex ];
|
|
746
|
+
|
|
747
|
+
var endOfFace = false;
|
|
748
|
+
|
|
749
|
+
if ( vertexIndex < 0 ) {
|
|
750
|
+
|
|
751
|
+
vertexIndex = vertexIndex ^ - 1;
|
|
752
|
+
indexBuffer[ polygonVertexIndex ] = vertexIndex;
|
|
753
|
+
endOfFace = true;
|
|
754
|
+
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
var vertex = new Vertex();
|
|
758
|
+
var weightIndices = [];
|
|
759
|
+
var weights = [];
|
|
760
|
+
|
|
761
|
+
vertex.position.fromArray( vertexBuffer, vertexIndex * 3 );
|
|
762
|
+
|
|
763
|
+
if ( deformer ) {
|
|
764
|
+
|
|
765
|
+
var subDeformers = deformer.map;
|
|
766
|
+
|
|
767
|
+
for ( var key in subDeformers ) {
|
|
768
|
+
|
|
769
|
+
var subDeformer = subDeformers[ key ];
|
|
770
|
+
var indices = subDeformer.indices;
|
|
771
|
+
|
|
772
|
+
for ( var j = 0; j < indices.length; j ++ ) {
|
|
773
|
+
|
|
774
|
+
var index = indices[ j ];
|
|
775
|
+
|
|
776
|
+
if ( index === vertexIndex ) {
|
|
777
|
+
|
|
778
|
+
weights.push( subDeformer.weights[ j ] );
|
|
779
|
+
weightIndices.push( subDeformer.index );
|
|
780
|
+
|
|
781
|
+
break;
|
|
782
|
+
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if ( weights.length > 4 ) {
|
|
790
|
+
|
|
791
|
+
console.warn( 'FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.' );
|
|
792
|
+
|
|
793
|
+
var WIndex = [ 0, 0, 0, 0 ];
|
|
794
|
+
var Weight = [ 0, 0, 0, 0 ];
|
|
795
|
+
|
|
796
|
+
weights.forEach( function ( weight, weightIndex ) {
|
|
797
|
+
|
|
798
|
+
var currentWeight = weight;
|
|
799
|
+
var currentIndex = weightIndices[ weightIndex ];
|
|
800
|
+
|
|
801
|
+
Weight.forEach( function ( comparedWeight, comparedWeightIndex, comparedWeightArray ) {
|
|
802
|
+
|
|
803
|
+
if ( currentWeight > comparedWeight ) {
|
|
804
|
+
|
|
805
|
+
comparedWeightArray[ comparedWeightIndex ] = currentWeight;
|
|
806
|
+
currentWeight = comparedWeight;
|
|
807
|
+
|
|
808
|
+
var tmp = WIndex[ comparedWeightIndex ];
|
|
809
|
+
WIndex[ comparedWeightIndex ] = currentIndex;
|
|
810
|
+
currentIndex = tmp;
|
|
811
|
+
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
} );
|
|
815
|
+
|
|
816
|
+
} );
|
|
817
|
+
|
|
818
|
+
weightIndices = WIndex;
|
|
819
|
+
weights = Weight;
|
|
820
|
+
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
for ( var i = weights.length; i < 4; ++ i ) {
|
|
824
|
+
|
|
825
|
+
weights[ i ] = 0;
|
|
826
|
+
weightIndices[ i ] = 0;
|
|
827
|
+
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
vertex.skinWeights.fromArray( weights );
|
|
831
|
+
vertex.skinIndices.fromArray( weightIndices );
|
|
832
|
+
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
if ( normalInfo ) {
|
|
836
|
+
|
|
837
|
+
vertex.normal.fromArray( getData( polygonVertexIndex, polygonIndex, vertexIndex, normalInfo ) );
|
|
838
|
+
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if ( uvInfo ) {
|
|
842
|
+
|
|
843
|
+
vertex.uv.fromArray( getData( polygonVertexIndex, polygonIndex, vertexIndex, uvInfo ) );
|
|
844
|
+
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if ( uv2Info ) {
|
|
848
|
+
|
|
849
|
+
vertex.uv2.fromArray( getData( polygonVertexIndex, polygonIndex, vertexIndex, uv2Info ) );
|
|
850
|
+
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if ( colorInfo ) {
|
|
854
|
+
|
|
855
|
+
vertex.color.fromArray( getData( polygonVertexIndex, polygonIndex, vertexIndex, colorInfo ) );
|
|
856
|
+
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
faceVertexBuffer.push( vertex );
|
|
860
|
+
|
|
861
|
+
if ( endOfFace ) {
|
|
862
|
+
|
|
863
|
+
var face = new Face();
|
|
864
|
+
face.genTrianglesFromVertices( faceVertexBuffer );
|
|
865
|
+
|
|
866
|
+
if ( materialInfo !== undefined ) {
|
|
867
|
+
|
|
868
|
+
var materials = getData( polygonVertexIndex, polygonIndex, vertexIndex, materialInfo );
|
|
869
|
+
face.materialIndex = materials[ 0 ];
|
|
870
|
+
|
|
871
|
+
} else {
|
|
872
|
+
|
|
873
|
+
// Seems like some models don't have materialInfo(subNodes.LayerElementMaterial).
|
|
874
|
+
// Set 0 in such a case.
|
|
875
|
+
face.materialIndex = 0;
|
|
876
|
+
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
geometry.faces.push( face );
|
|
880
|
+
faceVertexBuffer = [];
|
|
881
|
+
polygonIndex ++;
|
|
882
|
+
|
|
883
|
+
endOfFace = false;
|
|
884
|
+
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* @type {{vertexBuffer: number[], normalBuffer: number[], uvBuffer: number[], skinIndexBuffer: number[], skinWeightBuffer: number[], materialIndexBuffer: number[]}}
|
|
891
|
+
*/
|
|
892
|
+
var bufferInfo = geometry.flattenToBuffers();
|
|
893
|
+
|
|
894
|
+
var geo = new THREE.BufferGeometry();
|
|
895
|
+
geo.name = geometryNode.name;
|
|
896
|
+
geo.addAttribute( 'position', new THREE.Float32BufferAttribute( bufferInfo.vertexBuffer, 3 ) );
|
|
897
|
+
|
|
898
|
+
if ( bufferInfo.normalBuffer.length > 0 ) {
|
|
899
|
+
|
|
900
|
+
geo.addAttribute( 'normal', new THREE.Float32BufferAttribute( bufferInfo.normalBuffer, 3 ) );
|
|
901
|
+
|
|
902
|
+
}
|
|
903
|
+
if ( bufferInfo.uvBuffer.length > 0 ) {
|
|
904
|
+
|
|
905
|
+
geo.addAttribute( 'uv', new THREE.Float32BufferAttribute( bufferInfo.uvBuffer, 2 ) );
|
|
906
|
+
|
|
907
|
+
}
|
|
908
|
+
if ( bufferInfo.uv2Buffer.length > 0 ) {
|
|
909
|
+
|
|
910
|
+
geo.addAttribute( 'uv2', new THREE.Float32BufferAttribute( bufferInfo.uv2Buffer, 2 ) );
|
|
911
|
+
|
|
912
|
+
}
|
|
913
|
+
if ( subNodes.LayerElementColor ) {
|
|
914
|
+
|
|
915
|
+
geo.addAttribute( 'color', new THREE.Float32BufferAttribute( bufferInfo.colorBuffer, 3 ) );
|
|
916
|
+
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
if ( deformer ) {
|
|
920
|
+
|
|
921
|
+
geo.addAttribute( 'skinIndex', new THREE.Float32BufferAttribute( bufferInfo.skinIndexBuffer, 4 ) );
|
|
922
|
+
|
|
923
|
+
geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( bufferInfo.skinWeightBuffer, 4 ) );
|
|
924
|
+
|
|
925
|
+
geo.FBX_Deformer = deformer;
|
|
926
|
+
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// Convert the material indices of each vertex into rendering groups on the geometry.
|
|
930
|
+
|
|
931
|
+
var materialIndexBuffer = bufferInfo.materialIndexBuffer;
|
|
932
|
+
var prevMaterialIndex = materialIndexBuffer[ 0 ];
|
|
933
|
+
var startIndex = 0;
|
|
934
|
+
|
|
935
|
+
for ( var i = 0; i < materialIndexBuffer.length; ++ i ) {
|
|
936
|
+
|
|
937
|
+
if ( materialIndexBuffer[ i ] !== prevMaterialIndex ) {
|
|
938
|
+
|
|
939
|
+
geo.addGroup( startIndex, i - startIndex, prevMaterialIndex );
|
|
940
|
+
|
|
941
|
+
prevMaterialIndex = materialIndexBuffer[ i ];
|
|
942
|
+
startIndex = i;
|
|
943
|
+
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
return geo;
|
|
949
|
+
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* Parses normal information for geometry.
|
|
954
|
+
* @param {FBXGeometryNode} geometryNode
|
|
955
|
+
* @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}}
|
|
956
|
+
*/
|
|
957
|
+
function getNormals( NormalNode ) {
|
|
958
|
+
|
|
959
|
+
var mappingType = NormalNode.properties.MappingInformationType;
|
|
960
|
+
var referenceType = NormalNode.properties.ReferenceInformationType;
|
|
961
|
+
var buffer = parseFloatArray( NormalNode.subNodes.Normals.properties.a );
|
|
962
|
+
var indexBuffer = [];
|
|
963
|
+
if ( referenceType === 'IndexToDirect' ) {
|
|
964
|
+
|
|
965
|
+
if ( 'NormalIndex' in NormalNode.subNodes ) {
|
|
966
|
+
|
|
967
|
+
indexBuffer = parseIntArray( NormalNode.subNodes.NormalIndex.properties.a );
|
|
968
|
+
|
|
969
|
+
} else if ( 'NormalsIndex' in NormalNode.subNodes ) {
|
|
970
|
+
|
|
971
|
+
indexBuffer = parseIntArray( NormalNode.subNodes.NormalsIndex.properties.a );
|
|
972
|
+
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
return {
|
|
978
|
+
dataSize: 3,
|
|
979
|
+
buffer: buffer,
|
|
980
|
+
indices: indexBuffer,
|
|
981
|
+
mappingType: mappingType,
|
|
982
|
+
referenceType: referenceType
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* Parses UV information for geometry.
|
|
989
|
+
* @param {FBXGeometryNode} geometryNode
|
|
990
|
+
* @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}}
|
|
991
|
+
*/
|
|
992
|
+
function getUVs( UVNode ) {
|
|
993
|
+
|
|
994
|
+
var mappingType = UVNode.properties.MappingInformationType;
|
|
995
|
+
var referenceType = UVNode.properties.ReferenceInformationType;
|
|
996
|
+
var buffer = parseFloatArray( UVNode.subNodes.UV.properties.a );
|
|
997
|
+
var indexBuffer = [];
|
|
998
|
+
if ( referenceType === 'IndexToDirect' ) {
|
|
999
|
+
|
|
1000
|
+
indexBuffer = parseIntArray( UVNode.subNodes.UVIndex.properties.a );
|
|
1001
|
+
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
return {
|
|
1005
|
+
dataSize: 2,
|
|
1006
|
+
buffer: buffer,
|
|
1007
|
+
indices: indexBuffer,
|
|
1008
|
+
mappingType: mappingType,
|
|
1009
|
+
referenceType: referenceType
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* Parses Vertex Color information for geometry.
|
|
1016
|
+
* @param {FBXGeometryNode} geometryNode
|
|
1017
|
+
* @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}}
|
|
1018
|
+
*/
|
|
1019
|
+
function getColors( ColorNode ) {
|
|
1020
|
+
|
|
1021
|
+
var mappingType = ColorNode.properties.MappingInformationType;
|
|
1022
|
+
var referenceType = ColorNode.properties.ReferenceInformationType;
|
|
1023
|
+
var buffer = parseFloatArray( ColorNode.subNodes.Colors.properties.a );
|
|
1024
|
+
var indexBuffer = [];
|
|
1025
|
+
if ( referenceType === 'IndexToDirect' ) {
|
|
1026
|
+
|
|
1027
|
+
indexBuffer = parseFloatArray( ColorNode.subNodes.ColorIndex.properties.a );
|
|
1028
|
+
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
return {
|
|
1032
|
+
dataSize: 4,
|
|
1033
|
+
buffer: buffer,
|
|
1034
|
+
indices: indexBuffer,
|
|
1035
|
+
mappingType: mappingType,
|
|
1036
|
+
referenceType: referenceType
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Parses material application information for geometry.
|
|
1043
|
+
* @param {FBXGeometryNode}
|
|
1044
|
+
* @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}}
|
|
1045
|
+
*/
|
|
1046
|
+
function getMaterials( MaterialNode ) {
|
|
1047
|
+
|
|
1048
|
+
var mappingType = MaterialNode.properties.MappingInformationType;
|
|
1049
|
+
var referenceType = MaterialNode.properties.ReferenceInformationType;
|
|
1050
|
+
|
|
1051
|
+
if ( mappingType === 'NoMappingInformation' ) {
|
|
1052
|
+
|
|
1053
|
+
return {
|
|
1054
|
+
dataSize: 1,
|
|
1055
|
+
buffer: [ 0 ],
|
|
1056
|
+
indices: [ 0 ],
|
|
1057
|
+
mappingType: 'AllSame',
|
|
1058
|
+
referenceType: referenceType
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
var materialIndexBuffer = parseIntArray( MaterialNode.subNodes.Materials.properties.a );
|
|
1064
|
+
|
|
1065
|
+
// Since materials are stored as indices, there's a bit of a mismatch between FBX and what
|
|
1066
|
+
// we expect. So we create an intermediate buffer that points to the index in the buffer,
|
|
1067
|
+
// for conforming with the other functions we've written for other data.
|
|
1068
|
+
var materialIndices = [];
|
|
1069
|
+
|
|
1070
|
+
for ( var materialIndexBufferIndex = 0, materialIndexBufferLength = materialIndexBuffer.length; materialIndexBufferIndex < materialIndexBufferLength; ++ materialIndexBufferIndex ) {
|
|
1071
|
+
|
|
1072
|
+
materialIndices.push( materialIndexBufferIndex );
|
|
1073
|
+
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
return {
|
|
1077
|
+
dataSize: 1,
|
|
1078
|
+
buffer: materialIndexBuffer,
|
|
1079
|
+
indices: materialIndices,
|
|
1080
|
+
mappingType: mappingType,
|
|
1081
|
+
referenceType: referenceType
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* Function uses the infoObject and given indices to return value array of object.
|
|
1088
|
+
* @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
|
|
1089
|
+
* @param {number} polygonIndex - Index of polygon in geometry.
|
|
1090
|
+
* @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
|
|
1091
|
+
* @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
|
|
1092
|
+
* @returns {number[]}
|
|
1093
|
+
*/
|
|
1094
|
+
|
|
1095
|
+
var dataArray = [];
|
|
1096
|
+
|
|
1097
|
+
var GetData = {
|
|
1098
|
+
|
|
1099
|
+
ByPolygonVertex: {
|
|
1100
|
+
|
|
1101
|
+
/**
|
|
1102
|
+
* Function uses the infoObject and given indices to return value array of object.
|
|
1103
|
+
* @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
|
|
1104
|
+
* @param {number} polygonIndex - Index of polygon in geometry.
|
|
1105
|
+
* @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
|
|
1106
|
+
* @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
|
|
1107
|
+
* @returns {number[]}
|
|
1108
|
+
*/
|
|
1109
|
+
Direct: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
|
|
1110
|
+
|
|
1111
|
+
var from = ( polygonVertexIndex * infoObject.dataSize );
|
|
1112
|
+
var to = ( polygonVertexIndex * infoObject.dataSize ) + infoObject.dataSize;
|
|
1113
|
+
|
|
1114
|
+
// return infoObject.buffer.slice( from, to );
|
|
1115
|
+
return slice( dataArray, infoObject.buffer, from, to );
|
|
1116
|
+
|
|
1117
|
+
},
|
|
1118
|
+
|
|
1119
|
+
/**
|
|
1120
|
+
* Function uses the infoObject and given indices to return value array of object.
|
|
1121
|
+
* @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
|
|
1122
|
+
* @param {number} polygonIndex - Index of polygon in geometry.
|
|
1123
|
+
* @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
|
|
1124
|
+
* @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
|
|
1125
|
+
* @returns {number[]}
|
|
1126
|
+
*/
|
|
1127
|
+
IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
|
|
1128
|
+
|
|
1129
|
+
var index = infoObject.indices[ polygonVertexIndex ];
|
|
1130
|
+
var from = ( index * infoObject.dataSize );
|
|
1131
|
+
var to = ( index * infoObject.dataSize ) + infoObject.dataSize;
|
|
1132
|
+
|
|
1133
|
+
// return infoObject.buffer.slice( from, to );
|
|
1134
|
+
return slice( dataArray, infoObject.buffer, from, to );
|
|
1135
|
+
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
},
|
|
1139
|
+
|
|
1140
|
+
ByPolygon: {
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* Function uses the infoObject and given indices to return value array of object.
|
|
1144
|
+
* @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
|
|
1145
|
+
* @param {number} polygonIndex - Index of polygon in geometry.
|
|
1146
|
+
* @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
|
|
1147
|
+
* @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
|
|
1148
|
+
* @returns {number[]}
|
|
1149
|
+
*/
|
|
1150
|
+
Direct: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
|
|
1151
|
+
|
|
1152
|
+
var from = polygonIndex * infoObject.dataSize;
|
|
1153
|
+
var to = polygonIndex * infoObject.dataSize + infoObject.dataSize;
|
|
1154
|
+
|
|
1155
|
+
// return infoObject.buffer.slice( from, to );
|
|
1156
|
+
return slice( dataArray, infoObject.buffer, from, to );
|
|
1157
|
+
|
|
1158
|
+
},
|
|
1159
|
+
|
|
1160
|
+
/**
|
|
1161
|
+
* Function uses the infoObject and given indices to return value array of object.
|
|
1162
|
+
* @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
|
|
1163
|
+
* @param {number} polygonIndex - Index of polygon in geometry.
|
|
1164
|
+
* @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
|
|
1165
|
+
* @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
|
|
1166
|
+
* @returns {number[]}
|
|
1167
|
+
*/
|
|
1168
|
+
IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
|
|
1169
|
+
|
|
1170
|
+
var index = infoObject.indices[ polygonIndex ];
|
|
1171
|
+
var from = index * infoObject.dataSize;
|
|
1172
|
+
var to = index * infoObject.dataSize + infoObject.dataSize;
|
|
1173
|
+
|
|
1174
|
+
// return infoObject.buffer.slice( from, to );
|
|
1175
|
+
return slice( dataArray, infoObject.buffer, from, to );
|
|
1176
|
+
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
},
|
|
1180
|
+
|
|
1181
|
+
ByVertice: {
|
|
1182
|
+
|
|
1183
|
+
Direct: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
|
|
1184
|
+
|
|
1185
|
+
var from = ( vertexIndex * infoObject.dataSize );
|
|
1186
|
+
var to = ( vertexIndex * infoObject.dataSize ) + infoObject.dataSize;
|
|
1187
|
+
|
|
1188
|
+
// return infoObject.buffer.slice( from, to );
|
|
1189
|
+
return slice( dataArray, infoObject.buffer, from, to );
|
|
1190
|
+
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
},
|
|
1194
|
+
|
|
1195
|
+
AllSame: {
|
|
1196
|
+
|
|
1197
|
+
/**
|
|
1198
|
+
* Function uses the infoObject and given indices to return value array of object.
|
|
1199
|
+
* @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex).
|
|
1200
|
+
* @param {number} polygonIndex - Index of polygon in geometry.
|
|
1201
|
+
* @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore).
|
|
1202
|
+
* @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data.
|
|
1203
|
+
* @returns {number[]}
|
|
1204
|
+
*/
|
|
1205
|
+
IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
|
|
1206
|
+
|
|
1207
|
+
var from = infoObject.indices[ 0 ] * infoObject.dataSize;
|
|
1208
|
+
var to = infoObject.indices[ 0 ] * infoObject.dataSize + infoObject.dataSize;
|
|
1209
|
+
|
|
1210
|
+
// return infoObject.buffer.slice( from, to );
|
|
1211
|
+
return slice( dataArray, infoObject.buffer, from, to );
|
|
1212
|
+
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1219
|
+
function getData( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
|
|
1220
|
+
|
|
1221
|
+
return GetData[ infoObject.mappingType ][ infoObject.referenceType ]( polygonVertexIndex, polygonIndex, vertexIndex, infoObject );
|
|
1222
|
+
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/**
|
|
1226
|
+
* Specialty function for parsing NurbsCurve based Geometry Nodes.
|
|
1227
|
+
* @param {FBXGeometryNode} geometryNode
|
|
1228
|
+
* @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} relationships
|
|
1229
|
+
* @returns {THREE.BufferGeometry}
|
|
1230
|
+
*/
|
|
1231
|
+
function parseNurbsGeometry( geometryNode ) {
|
|
1232
|
+
|
|
1233
|
+
if ( THREE.NURBSCurve === undefined ) {
|
|
1234
|
+
|
|
1235
|
+
console.error( "THREE.FBXLoader relies on THREE.NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry." );
|
|
1236
|
+
return new THREE.BufferGeometry();
|
|
1237
|
+
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
var order = parseInt( geometryNode.properties.Order );
|
|
1241
|
+
|
|
1242
|
+
if ( isNaN( order ) ) {
|
|
1243
|
+
|
|
1244
|
+
console.error( "FBXLoader: Invalid Order " + geometryNode.properties.Order + " given for geometry ID: " + geometryNode.id );
|
|
1245
|
+
return new THREE.BufferGeometry();
|
|
1246
|
+
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
var degree = order - 1;
|
|
1250
|
+
|
|
1251
|
+
var knots = parseFloatArray( geometryNode.subNodes.KnotVector.properties.a );
|
|
1252
|
+
var controlPoints = [];
|
|
1253
|
+
var pointsValues = parseFloatArray( geometryNode.subNodes.Points.properties.a );
|
|
1254
|
+
|
|
1255
|
+
for ( var i = 0, l = pointsValues.length; i < l; i += 4 ) {
|
|
1256
|
+
|
|
1257
|
+
controlPoints.push( new THREE.Vector4().fromArray( pointsValues, i ) );
|
|
1258
|
+
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
var startKnot, endKnot;
|
|
1262
|
+
|
|
1263
|
+
if ( geometryNode.properties.Form === 'Closed' ) {
|
|
1264
|
+
|
|
1265
|
+
controlPoints.push( controlPoints[ 0 ] );
|
|
1266
|
+
|
|
1267
|
+
} else if ( geometryNode.properties.Form === 'Periodic' ) {
|
|
1268
|
+
|
|
1269
|
+
startKnot = degree;
|
|
1270
|
+
endKnot = knots.length - 1 - startKnot;
|
|
1271
|
+
|
|
1272
|
+
for ( var i = 0; i < degree; ++ i ) {
|
|
1273
|
+
|
|
1274
|
+
controlPoints.push( controlPoints[ i ] );
|
|
1275
|
+
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
var curve = new THREE.NURBSCurve( degree, knots, controlPoints, startKnot, endKnot );
|
|
1281
|
+
var vertices = curve.getPoints( controlPoints.length * 7 );
|
|
1282
|
+
|
|
1283
|
+
var positions = new Float32Array( vertices.length * 3 );
|
|
1284
|
+
|
|
1285
|
+
for ( var i = 0, l = vertices.length; i < l; ++ i ) {
|
|
1286
|
+
|
|
1287
|
+
vertices[ i ].toArray( positions, i * 3 );
|
|
1288
|
+
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
var geometry = new THREE.BufferGeometry();
|
|
1292
|
+
geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
|
|
1293
|
+
|
|
1294
|
+
return geometry;
|
|
1295
|
+
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/**
|
|
1299
|
+
* Finally generates Scene graph and Scene graph Objects.
|
|
1300
|
+
* @param {{Objects: {subNodes: {Model: Object.<number, FBXModelNode>}}}} FBXTree
|
|
1301
|
+
* @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
|
|
1302
|
+
* @param {Map<number, {map: Map<number, {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}>, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}>} deformers
|
|
1303
|
+
* @param {Map<number, THREE.BufferGeometry>} geometryMap
|
|
1304
|
+
* @param {Map<number, THREE.Material>} materialMap
|
|
1305
|
+
* @returns {THREE.Group}
|
|
1306
|
+
*/
|
|
1307
|
+
function parseScene( FBXTree, connections, deformers, geometryMap, materialMap ) {
|
|
1308
|
+
|
|
1309
|
+
var sceneGraph = new THREE.Group();
|
|
1310
|
+
|
|
1311
|
+
var ModelNode = FBXTree.Objects.subNodes.Model;
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* @type {Array.<THREE.Object3D>}
|
|
1315
|
+
*/
|
|
1316
|
+
var modelArray = [];
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* @type {Map.<number, THREE.Object3D>}
|
|
1320
|
+
*/
|
|
1321
|
+
var modelMap = new Map();
|
|
1322
|
+
|
|
1323
|
+
for ( var nodeID in ModelNode ) {
|
|
1324
|
+
|
|
1325
|
+
var id = parseInt( nodeID );
|
|
1326
|
+
var node = ModelNode[ nodeID ];
|
|
1327
|
+
var conns = connections.get( id );
|
|
1328
|
+
var model = null;
|
|
1329
|
+
|
|
1330
|
+
for ( var i = 0; i < conns.parents.length; ++ i ) {
|
|
1331
|
+
|
|
1332
|
+
for ( var FBX_ID in deformers ) {
|
|
1333
|
+
|
|
1334
|
+
var deformer = deformers[ FBX_ID ];
|
|
1335
|
+
var subDeformers = deformer.map;
|
|
1336
|
+
var subDeformer = subDeformers[ conns.parents[ i ].ID ];
|
|
1337
|
+
|
|
1338
|
+
if ( subDeformer ) {
|
|
1339
|
+
|
|
1340
|
+
var model2 = model;
|
|
1341
|
+
model = new THREE.Bone();
|
|
1342
|
+
deformer.bones[ subDeformer.index ] = model;
|
|
1343
|
+
|
|
1344
|
+
// seems like we need this not to make non-connected bone, maybe?
|
|
1345
|
+
// TODO: confirm
|
|
1346
|
+
if ( model2 !== null ) model.add( model2 );
|
|
1347
|
+
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
if ( ! model ) {
|
|
1355
|
+
|
|
1356
|
+
switch ( node.attrType ) {
|
|
1357
|
+
|
|
1358
|
+
case "Mesh":
|
|
1359
|
+
/**
|
|
1360
|
+
* @type {?THREE.BufferGeometry}
|
|
1361
|
+
*/
|
|
1362
|
+
var geometry = null;
|
|
1363
|
+
|
|
1364
|
+
/**
|
|
1365
|
+
* @type {THREE.MultiMaterial|THREE.Material}
|
|
1366
|
+
*/
|
|
1367
|
+
var material = null;
|
|
1368
|
+
|
|
1369
|
+
/**
|
|
1370
|
+
* @type {Array.<THREE.Material>}
|
|
1371
|
+
*/
|
|
1372
|
+
var materials = [];
|
|
1373
|
+
|
|
1374
|
+
for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
1375
|
+
|
|
1376
|
+
var child = conns.children[ childrenIndex ];
|
|
1377
|
+
|
|
1378
|
+
if ( geometryMap.has( child.ID ) ) {
|
|
1379
|
+
|
|
1380
|
+
geometry = geometryMap.get( child.ID );
|
|
1381
|
+
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
if ( materialMap.has( child.ID ) ) {
|
|
1385
|
+
|
|
1386
|
+
materials.push( materialMap.get( child.ID ) );
|
|
1387
|
+
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
}
|
|
1391
|
+
if ( materials.length > 1 ) {
|
|
1392
|
+
|
|
1393
|
+
material = materials;
|
|
1394
|
+
|
|
1395
|
+
} else if ( materials.length > 0 ) {
|
|
1396
|
+
|
|
1397
|
+
material = materials[ 0 ];
|
|
1398
|
+
|
|
1399
|
+
} else {
|
|
1400
|
+
|
|
1401
|
+
material = new THREE.MeshBasicMaterial( { color: 0x3300ff } );
|
|
1402
|
+
materials.push( material );
|
|
1403
|
+
|
|
1404
|
+
}
|
|
1405
|
+
if ( 'color' in geometry.attributes ) {
|
|
1406
|
+
|
|
1407
|
+
for ( var materialIndex = 0, numMaterials = materials.length; materialIndex < numMaterials; ++materialIndex ) {
|
|
1408
|
+
|
|
1409
|
+
materials[ materialIndex ].vertexColors = THREE.VertexColors;
|
|
1410
|
+
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
}
|
|
1414
|
+
if ( geometry.FBX_Deformer ) {
|
|
1415
|
+
|
|
1416
|
+
for ( var materialsIndex = 0, materialsLength = materials.length; materialsIndex < materialsLength; ++ materialsIndex ) {
|
|
1417
|
+
|
|
1418
|
+
materials[ materialsIndex ].skinning = true;
|
|
1419
|
+
|
|
1420
|
+
}
|
|
1421
|
+
model = new THREE.SkinnedMesh( geometry, material );
|
|
1422
|
+
|
|
1423
|
+
} else {
|
|
1424
|
+
|
|
1425
|
+
model = new THREE.Mesh( geometry, material );
|
|
1426
|
+
|
|
1427
|
+
}
|
|
1428
|
+
break;
|
|
1429
|
+
|
|
1430
|
+
case "NurbsCurve":
|
|
1431
|
+
var geometry = null;
|
|
1432
|
+
|
|
1433
|
+
for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
1434
|
+
|
|
1435
|
+
var child = conns.children[ childrenIndex ];
|
|
1436
|
+
|
|
1437
|
+
if ( geometryMap.has( child.ID ) ) {
|
|
1438
|
+
|
|
1439
|
+
geometry = geometryMap.get( child.ID );
|
|
1440
|
+
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
// FBX does not list materials for Nurbs lines, so we'll just put our own in here.
|
|
1446
|
+
material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 5 } );
|
|
1447
|
+
model = new THREE.Line( geometry, material );
|
|
1448
|
+
break;
|
|
1449
|
+
|
|
1450
|
+
default:
|
|
1451
|
+
model = new THREE.Object3D();
|
|
1452
|
+
break;
|
|
1453
|
+
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
model.name = node.attrName.replace( /:/, '' ).replace( /_/, '' ).replace( /-/, '' );
|
|
1459
|
+
model.FBX_ID = id;
|
|
1460
|
+
|
|
1461
|
+
modelArray.push( model );
|
|
1462
|
+
modelMap.set( id, model );
|
|
1463
|
+
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
for ( var modelArrayIndex = 0, modelArrayLength = modelArray.length; modelArrayIndex < modelArrayLength; ++ modelArrayIndex ) {
|
|
1467
|
+
|
|
1468
|
+
var model = modelArray[ modelArrayIndex ];
|
|
1469
|
+
|
|
1470
|
+
var node = ModelNode[ model.FBX_ID ];
|
|
1471
|
+
|
|
1472
|
+
if ( 'Lcl_Translation' in node.properties ) {
|
|
1473
|
+
|
|
1474
|
+
model.position.fromArray( parseFloatArray( node.properties.Lcl_Translation.value ) );
|
|
1475
|
+
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
if ( 'Lcl_Rotation' in node.properties ) {
|
|
1479
|
+
|
|
1480
|
+
var rotation = parseFloatArray( node.properties.Lcl_Rotation.value ).map( degreeToRadian );
|
|
1481
|
+
rotation.push( 'ZYX' );
|
|
1482
|
+
model.rotation.fromArray( rotation );
|
|
1483
|
+
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
if ( 'Lcl_Scaling' in node.properties ) {
|
|
1487
|
+
|
|
1488
|
+
model.scale.fromArray( parseFloatArray( node.properties.Lcl_Scaling.value ) );
|
|
1489
|
+
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
if ( 'PreRotation' in node.properties ) {
|
|
1493
|
+
|
|
1494
|
+
var preRotations = new THREE.Euler().setFromVector3( parseVector3( node.properties.PreRotation ).multiplyScalar( DEG2RAD ), 'ZYX' );
|
|
1495
|
+
preRotations = new THREE.Quaternion().setFromEuler( preRotations );
|
|
1496
|
+
var currentRotation = new THREE.Quaternion().setFromEuler( model.rotation );
|
|
1497
|
+
preRotations.multiply( currentRotation );
|
|
1498
|
+
model.rotation.setFromQuaternion( preRotations, 'ZYX' );
|
|
1499
|
+
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
var conns = connections.get( model.FBX_ID );
|
|
1503
|
+
for ( var parentIndex = 0; parentIndex < conns.parents.length; parentIndex ++ ) {
|
|
1504
|
+
|
|
1505
|
+
var pIndex = findIndex( modelArray, function ( mod ) {
|
|
1506
|
+
|
|
1507
|
+
return mod.FBX_ID === conns.parents[ parentIndex ].ID;
|
|
1508
|
+
|
|
1509
|
+
} );
|
|
1510
|
+
if ( pIndex > - 1 ) {
|
|
1511
|
+
|
|
1512
|
+
modelArray[ pIndex ].add( model );
|
|
1513
|
+
break;
|
|
1514
|
+
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
}
|
|
1518
|
+
if ( model.parent === null ) {
|
|
1519
|
+
|
|
1520
|
+
sceneGraph.add( model );
|
|
1521
|
+
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
|
|
1527
|
+
// Now with the bones created, we can update the skeletons and bind them to the skinned meshes.
|
|
1528
|
+
sceneGraph.updateMatrixWorld( true );
|
|
1529
|
+
|
|
1530
|
+
// Put skeleton into bind pose.
|
|
1531
|
+
var BindPoseNode = FBXTree.Objects.subNodes.Pose;
|
|
1532
|
+
for ( var nodeID in BindPoseNode ) {
|
|
1533
|
+
|
|
1534
|
+
if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) {
|
|
1535
|
+
|
|
1536
|
+
BindPoseNode = BindPoseNode[ nodeID ];
|
|
1537
|
+
break;
|
|
1538
|
+
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
}
|
|
1542
|
+
if ( BindPoseNode ) {
|
|
1543
|
+
|
|
1544
|
+
var PoseNode = BindPoseNode.subNodes.PoseNode;
|
|
1545
|
+
var worldMatrices = new Map();
|
|
1546
|
+
|
|
1547
|
+
for ( var PoseNodeIndex = 0, PoseNodeLength = PoseNode.length; PoseNodeIndex < PoseNodeLength; ++ PoseNodeIndex ) {
|
|
1548
|
+
|
|
1549
|
+
var node = PoseNode[ PoseNodeIndex ];
|
|
1550
|
+
|
|
1551
|
+
var rawMatWrd = parseMatrixArray( node.subNodes.Matrix.properties.a );
|
|
1552
|
+
|
|
1553
|
+
worldMatrices.set( parseInt( node.id ), rawMatWrd );
|
|
1554
|
+
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
for ( var FBX_ID in deformers ) {
|
|
1560
|
+
|
|
1561
|
+
var deformer = deformers[ FBX_ID ];
|
|
1562
|
+
var subDeformers = deformer.map;
|
|
1563
|
+
|
|
1564
|
+
for ( var key in subDeformers ) {
|
|
1565
|
+
|
|
1566
|
+
var subDeformer = subDeformers[ key ];
|
|
1567
|
+
var subDeformerIndex = subDeformer.index;
|
|
1568
|
+
|
|
1569
|
+
/**
|
|
1570
|
+
* @type {THREE.Bone}
|
|
1571
|
+
*/
|
|
1572
|
+
var bone = deformer.bones[ subDeformerIndex ];
|
|
1573
|
+
if ( ! worldMatrices.has( bone.FBX_ID ) ) {
|
|
1574
|
+
|
|
1575
|
+
break;
|
|
1576
|
+
|
|
1577
|
+
}
|
|
1578
|
+
var mat = worldMatrices.get( bone.FBX_ID );
|
|
1579
|
+
bone.matrixWorld.copy( mat );
|
|
1580
|
+
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
// Now that skeleton is in bind pose, bind to model.
|
|
1584
|
+
deformer.skeleton = new THREE.Skeleton( deformer.bones );
|
|
1585
|
+
|
|
1586
|
+
var conns = connections.get( deformer.FBX_ID );
|
|
1587
|
+
var parents = conns.parents;
|
|
1588
|
+
|
|
1589
|
+
for ( var parentsIndex = 0, parentsLength = parents.length; parentsIndex < parentsLength; ++ parentsIndex ) {
|
|
1590
|
+
|
|
1591
|
+
var parent = parents[ parentsIndex ];
|
|
1592
|
+
|
|
1593
|
+
if ( geometryMap.has( parent.ID ) ) {
|
|
1594
|
+
|
|
1595
|
+
var geoID = parent.ID;
|
|
1596
|
+
var geoConns = connections.get( geoID );
|
|
1597
|
+
|
|
1598
|
+
for ( var i = 0; i < geoConns.parents.length; ++ i ) {
|
|
1599
|
+
|
|
1600
|
+
if ( modelMap.has( geoConns.parents[ i ].ID ) ) {
|
|
1601
|
+
|
|
1602
|
+
var model = modelMap.get( geoConns.parents[ i ].ID );
|
|
1603
|
+
//ASSERT model typeof SkinnedMesh
|
|
1604
|
+
model.bind( deformer.skeleton, model.matrixWorld );
|
|
1605
|
+
break;
|
|
1606
|
+
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
//Skeleton is now bound, return objects to starting
|
|
1618
|
+
//world positions.
|
|
1619
|
+
sceneGraph.updateMatrixWorld( true );
|
|
1620
|
+
|
|
1621
|
+
// Silly hack with the animation parsing. We're gonna pretend the scene graph has a skeleton
|
|
1622
|
+
// to attach animations to, since FBXs treat animations as animations for the entire scene,
|
|
1623
|
+
// not just for individual objects.
|
|
1624
|
+
sceneGraph.skeleton = {
|
|
1625
|
+
bones: modelArray
|
|
1626
|
+
};
|
|
1627
|
+
|
|
1628
|
+
var animations = parseAnimations( FBXTree, connections, sceneGraph );
|
|
1629
|
+
|
|
1630
|
+
addAnimations( sceneGraph, animations );
|
|
1631
|
+
|
|
1632
|
+
return sceneGraph;
|
|
1633
|
+
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
/**
|
|
1637
|
+
* Parses animation information from FBXTree and generates an AnimationInfoObject.
|
|
1638
|
+
* @param {{Objects: {subNodes: {AnimationCurveNode: any, AnimationCurve: any, AnimationLayer: any, AnimationStack: any}}}} FBXTree
|
|
1639
|
+
* @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
|
|
1640
|
+
*/
|
|
1641
|
+
function parseAnimations( FBXTree, connections, sceneGraph ) {
|
|
1642
|
+
|
|
1643
|
+
var rawNodes = FBXTree.Objects.subNodes.AnimationCurveNode;
|
|
1644
|
+
var rawCurves = FBXTree.Objects.subNodes.AnimationCurve;
|
|
1645
|
+
var rawLayers = FBXTree.Objects.subNodes.AnimationLayer;
|
|
1646
|
+
var rawStacks = FBXTree.Objects.subNodes.AnimationStack;
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* @type {{
|
|
1650
|
+
curves: Map<number, {
|
|
1651
|
+
T: {
|
|
1652
|
+
id: number;
|
|
1653
|
+
attr: string;
|
|
1654
|
+
internalID: number;
|
|
1655
|
+
attrX: boolean;
|
|
1656
|
+
attrY: boolean;
|
|
1657
|
+
attrZ: boolean;
|
|
1658
|
+
containerBoneID: number;
|
|
1659
|
+
containerID: number;
|
|
1660
|
+
curves: {
|
|
1661
|
+
x: {
|
|
1662
|
+
version: any;
|
|
1663
|
+
id: number;
|
|
1664
|
+
internalID: number;
|
|
1665
|
+
times: number[];
|
|
1666
|
+
values: number[];
|
|
1667
|
+
attrFlag: number[];
|
|
1668
|
+
attrData: number[];
|
|
1669
|
+
};
|
|
1670
|
+
y: {
|
|
1671
|
+
version: any;
|
|
1672
|
+
id: number;
|
|
1673
|
+
internalID: number;
|
|
1674
|
+
times: number[];
|
|
1675
|
+
values: number[];
|
|
1676
|
+
attrFlag: number[];
|
|
1677
|
+
attrData: number[];
|
|
1678
|
+
};
|
|
1679
|
+
z: {
|
|
1680
|
+
version: any;
|
|
1681
|
+
id: number;
|
|
1682
|
+
internalID: number;
|
|
1683
|
+
times: number[];
|
|
1684
|
+
values: number[];
|
|
1685
|
+
attrFlag: number[];
|
|
1686
|
+
attrData: number[];
|
|
1687
|
+
};
|
|
1688
|
+
};
|
|
1689
|
+
},
|
|
1690
|
+
R: {
|
|
1691
|
+
id: number;
|
|
1692
|
+
attr: string;
|
|
1693
|
+
internalID: number;
|
|
1694
|
+
attrX: boolean;
|
|
1695
|
+
attrY: boolean;
|
|
1696
|
+
attrZ: boolean;
|
|
1697
|
+
containerBoneID: number;
|
|
1698
|
+
containerID: number;
|
|
1699
|
+
curves: {
|
|
1700
|
+
x: {
|
|
1701
|
+
version: any;
|
|
1702
|
+
id: number;
|
|
1703
|
+
internalID: number;
|
|
1704
|
+
times: number[];
|
|
1705
|
+
values: number[];
|
|
1706
|
+
attrFlag: number[];
|
|
1707
|
+
attrData: number[];
|
|
1708
|
+
};
|
|
1709
|
+
y: {
|
|
1710
|
+
version: any;
|
|
1711
|
+
id: number;
|
|
1712
|
+
internalID: number;
|
|
1713
|
+
times: number[];
|
|
1714
|
+
values: number[];
|
|
1715
|
+
attrFlag: number[];
|
|
1716
|
+
attrData: number[];
|
|
1717
|
+
};
|
|
1718
|
+
z: {
|
|
1719
|
+
version: any;
|
|
1720
|
+
id: number;
|
|
1721
|
+
internalID: number;
|
|
1722
|
+
times: number[];
|
|
1723
|
+
values: number[];
|
|
1724
|
+
attrFlag: number[];
|
|
1725
|
+
attrData: number[];
|
|
1726
|
+
};
|
|
1727
|
+
};
|
|
1728
|
+
},
|
|
1729
|
+
S: {
|
|
1730
|
+
id: number;
|
|
1731
|
+
attr: string;
|
|
1732
|
+
internalID: number;
|
|
1733
|
+
attrX: boolean;
|
|
1734
|
+
attrY: boolean;
|
|
1735
|
+
attrZ: boolean;
|
|
1736
|
+
containerBoneID: number;
|
|
1737
|
+
containerID: number;
|
|
1738
|
+
curves: {
|
|
1739
|
+
x: {
|
|
1740
|
+
version: any;
|
|
1741
|
+
id: number;
|
|
1742
|
+
internalID: number;
|
|
1743
|
+
times: number[];
|
|
1744
|
+
values: number[];
|
|
1745
|
+
attrFlag: number[];
|
|
1746
|
+
attrData: number[];
|
|
1747
|
+
};
|
|
1748
|
+
y: {
|
|
1749
|
+
version: any;
|
|
1750
|
+
id: number;
|
|
1751
|
+
internalID: number;
|
|
1752
|
+
times: number[];
|
|
1753
|
+
values: number[];
|
|
1754
|
+
attrFlag: number[];
|
|
1755
|
+
attrData: number[];
|
|
1756
|
+
};
|
|
1757
|
+
z: {
|
|
1758
|
+
version: any;
|
|
1759
|
+
id: number;
|
|
1760
|
+
internalID: number;
|
|
1761
|
+
times: number[];
|
|
1762
|
+
values: number[];
|
|
1763
|
+
attrFlag: number[];
|
|
1764
|
+
attrData: number[];
|
|
1765
|
+
};
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
}>,
|
|
1769
|
+
layers: Map<number, {
|
|
1770
|
+
T: {
|
|
1771
|
+
id: number;
|
|
1772
|
+
attr: string;
|
|
1773
|
+
internalID: number;
|
|
1774
|
+
attrX: boolean;
|
|
1775
|
+
attrY: boolean;
|
|
1776
|
+
attrZ: boolean;
|
|
1777
|
+
containerBoneID: number;
|
|
1778
|
+
containerID: number;
|
|
1779
|
+
curves: {
|
|
1780
|
+
x: {
|
|
1781
|
+
version: any;
|
|
1782
|
+
id: number;
|
|
1783
|
+
internalID: number;
|
|
1784
|
+
times: number[];
|
|
1785
|
+
values: number[];
|
|
1786
|
+
attrFlag: number[];
|
|
1787
|
+
attrData: number[];
|
|
1788
|
+
};
|
|
1789
|
+
y: {
|
|
1790
|
+
version: any;
|
|
1791
|
+
id: number;
|
|
1792
|
+
internalID: number;
|
|
1793
|
+
times: number[];
|
|
1794
|
+
values: number[];
|
|
1795
|
+
attrFlag: number[];
|
|
1796
|
+
attrData: number[];
|
|
1797
|
+
};
|
|
1798
|
+
z: {
|
|
1799
|
+
version: any;
|
|
1800
|
+
id: number;
|
|
1801
|
+
internalID: number;
|
|
1802
|
+
times: number[];
|
|
1803
|
+
values: number[];
|
|
1804
|
+
attrFlag: number[];
|
|
1805
|
+
attrData: number[];
|
|
1806
|
+
};
|
|
1807
|
+
},
|
|
1808
|
+
},
|
|
1809
|
+
R: {
|
|
1810
|
+
id: number;
|
|
1811
|
+
attr: string;
|
|
1812
|
+
internalID: number;
|
|
1813
|
+
attrX: boolean;
|
|
1814
|
+
attrY: boolean;
|
|
1815
|
+
attrZ: boolean;
|
|
1816
|
+
containerBoneID: number;
|
|
1817
|
+
containerID: number;
|
|
1818
|
+
curves: {
|
|
1819
|
+
x: {
|
|
1820
|
+
version: any;
|
|
1821
|
+
id: number;
|
|
1822
|
+
internalID: number;
|
|
1823
|
+
times: number[];
|
|
1824
|
+
values: number[];
|
|
1825
|
+
attrFlag: number[];
|
|
1826
|
+
attrData: number[];
|
|
1827
|
+
};
|
|
1828
|
+
y: {
|
|
1829
|
+
version: any;
|
|
1830
|
+
id: number;
|
|
1831
|
+
internalID: number;
|
|
1832
|
+
times: number[];
|
|
1833
|
+
values: number[];
|
|
1834
|
+
attrFlag: number[];
|
|
1835
|
+
attrData: number[];
|
|
1836
|
+
};
|
|
1837
|
+
z: {
|
|
1838
|
+
version: any;
|
|
1839
|
+
id: number;
|
|
1840
|
+
internalID: number;
|
|
1841
|
+
times: number[];
|
|
1842
|
+
values: number[];
|
|
1843
|
+
attrFlag: number[];
|
|
1844
|
+
attrData: number[];
|
|
1845
|
+
};
|
|
1846
|
+
},
|
|
1847
|
+
},
|
|
1848
|
+
S: {
|
|
1849
|
+
id: number;
|
|
1850
|
+
attr: string;
|
|
1851
|
+
internalID: number;
|
|
1852
|
+
attrX: boolean;
|
|
1853
|
+
attrY: boolean;
|
|
1854
|
+
attrZ: boolean;
|
|
1855
|
+
containerBoneID: number;
|
|
1856
|
+
containerID: number;
|
|
1857
|
+
curves: {
|
|
1858
|
+
x: {
|
|
1859
|
+
version: any;
|
|
1860
|
+
id: number;
|
|
1861
|
+
internalID: number;
|
|
1862
|
+
times: number[];
|
|
1863
|
+
values: number[];
|
|
1864
|
+
attrFlag: number[];
|
|
1865
|
+
attrData: number[];
|
|
1866
|
+
};
|
|
1867
|
+
y: {
|
|
1868
|
+
version: any;
|
|
1869
|
+
id: number;
|
|
1870
|
+
internalID: number;
|
|
1871
|
+
times: number[];
|
|
1872
|
+
values: number[];
|
|
1873
|
+
attrFlag: number[];
|
|
1874
|
+
attrData: number[];
|
|
1875
|
+
};
|
|
1876
|
+
z: {
|
|
1877
|
+
version: any;
|
|
1878
|
+
id: number;
|
|
1879
|
+
internalID: number;
|
|
1880
|
+
times: number[];
|
|
1881
|
+
values: number[];
|
|
1882
|
+
attrFlag: number[];
|
|
1883
|
+
attrData: number[];
|
|
1884
|
+
};
|
|
1885
|
+
},
|
|
1886
|
+
}
|
|
1887
|
+
}[]>,
|
|
1888
|
+
stacks: Map<number, {
|
|
1889
|
+
name: string,
|
|
1890
|
+
layers: {
|
|
1891
|
+
T: {
|
|
1892
|
+
id: number;
|
|
1893
|
+
attr: string;
|
|
1894
|
+
internalID: number;
|
|
1895
|
+
attrX: boolean;
|
|
1896
|
+
attrY: boolean;
|
|
1897
|
+
attrZ: boolean;
|
|
1898
|
+
containerBoneID: number;
|
|
1899
|
+
containerID: number;
|
|
1900
|
+
curves: {
|
|
1901
|
+
x: {
|
|
1902
|
+
version: any;
|
|
1903
|
+
id: number;
|
|
1904
|
+
internalID: number;
|
|
1905
|
+
times: number[];
|
|
1906
|
+
values: number[];
|
|
1907
|
+
attrFlag: number[];
|
|
1908
|
+
attrData: number[];
|
|
1909
|
+
};
|
|
1910
|
+
y: {
|
|
1911
|
+
version: any;
|
|
1912
|
+
id: number;
|
|
1913
|
+
internalID: number;
|
|
1914
|
+
times: number[];
|
|
1915
|
+
values: number[];
|
|
1916
|
+
attrFlag: number[];
|
|
1917
|
+
attrData: number[];
|
|
1918
|
+
};
|
|
1919
|
+
z: {
|
|
1920
|
+
version: any;
|
|
1921
|
+
id: number;
|
|
1922
|
+
internalID: number;
|
|
1923
|
+
times: number[];
|
|
1924
|
+
values: number[];
|
|
1925
|
+
attrFlag: number[];
|
|
1926
|
+
attrData: number[];
|
|
1927
|
+
};
|
|
1928
|
+
};
|
|
1929
|
+
};
|
|
1930
|
+
R: {
|
|
1931
|
+
id: number;
|
|
1932
|
+
attr: string;
|
|
1933
|
+
internalID: number;
|
|
1934
|
+
attrX: boolean;
|
|
1935
|
+
attrY: boolean;
|
|
1936
|
+
attrZ: boolean;
|
|
1937
|
+
containerBoneID: number;
|
|
1938
|
+
containerID: number;
|
|
1939
|
+
curves: {
|
|
1940
|
+
x: {
|
|
1941
|
+
version: any;
|
|
1942
|
+
id: number;
|
|
1943
|
+
internalID: number;
|
|
1944
|
+
times: number[];
|
|
1945
|
+
values: number[];
|
|
1946
|
+
attrFlag: number[];
|
|
1947
|
+
attrData: number[];
|
|
1948
|
+
};
|
|
1949
|
+
y: {
|
|
1950
|
+
version: any;
|
|
1951
|
+
id: number;
|
|
1952
|
+
internalID: number;
|
|
1953
|
+
times: number[];
|
|
1954
|
+
values: number[];
|
|
1955
|
+
attrFlag: number[];
|
|
1956
|
+
attrData: number[];
|
|
1957
|
+
};
|
|
1958
|
+
z: {
|
|
1959
|
+
version: any;
|
|
1960
|
+
id: number;
|
|
1961
|
+
internalID: number;
|
|
1962
|
+
times: number[];
|
|
1963
|
+
values: number[];
|
|
1964
|
+
attrFlag: number[];
|
|
1965
|
+
attrData: number[];
|
|
1966
|
+
};
|
|
1967
|
+
};
|
|
1968
|
+
};
|
|
1969
|
+
S: {
|
|
1970
|
+
id: number;
|
|
1971
|
+
attr: string;
|
|
1972
|
+
internalID: number;
|
|
1973
|
+
attrX: boolean;
|
|
1974
|
+
attrY: boolean;
|
|
1975
|
+
attrZ: boolean;
|
|
1976
|
+
containerBoneID: number;
|
|
1977
|
+
containerID: number;
|
|
1978
|
+
curves: {
|
|
1979
|
+
x: {
|
|
1980
|
+
version: any;
|
|
1981
|
+
id: number;
|
|
1982
|
+
internalID: number;
|
|
1983
|
+
times: number[];
|
|
1984
|
+
values: number[];
|
|
1985
|
+
attrFlag: number[];
|
|
1986
|
+
attrData: number[];
|
|
1987
|
+
};
|
|
1988
|
+
y: {
|
|
1989
|
+
version: any;
|
|
1990
|
+
id: number;
|
|
1991
|
+
internalID: number;
|
|
1992
|
+
times: number[];
|
|
1993
|
+
values: number[];
|
|
1994
|
+
attrFlag: number[];
|
|
1995
|
+
attrData: number[];
|
|
1996
|
+
};
|
|
1997
|
+
z: {
|
|
1998
|
+
version: any;
|
|
1999
|
+
id: number;
|
|
2000
|
+
internalID: number;
|
|
2001
|
+
times: number[];
|
|
2002
|
+
values: number[];
|
|
2003
|
+
attrFlag: number[];
|
|
2004
|
+
attrData: number[];
|
|
2005
|
+
};
|
|
2006
|
+
};
|
|
2007
|
+
};
|
|
2008
|
+
}[][],
|
|
2009
|
+
length: number,
|
|
2010
|
+
frames: number }>,
|
|
2011
|
+
length: number,
|
|
2012
|
+
fps: number,
|
|
2013
|
+
frames: number
|
|
2014
|
+
}}
|
|
2015
|
+
*/
|
|
2016
|
+
var returnObject = {
|
|
2017
|
+
curves: new Map(),
|
|
2018
|
+
layers: {},
|
|
2019
|
+
stacks: {},
|
|
2020
|
+
length: 0,
|
|
2021
|
+
fps: 30,
|
|
2022
|
+
frames: 0
|
|
2023
|
+
};
|
|
2024
|
+
|
|
2025
|
+
/**
|
|
2026
|
+
* @type {Array.<{
|
|
2027
|
+
id: number;
|
|
2028
|
+
attr: string;
|
|
2029
|
+
internalID: number;
|
|
2030
|
+
attrX: boolean;
|
|
2031
|
+
attrY: boolean;
|
|
2032
|
+
attrZ: boolean;
|
|
2033
|
+
containerBoneID: number;
|
|
2034
|
+
containerID: number;
|
|
2035
|
+
}>}
|
|
2036
|
+
*/
|
|
2037
|
+
var animationCurveNodes = [];
|
|
2038
|
+
for ( var nodeID in rawNodes ) {
|
|
2039
|
+
|
|
2040
|
+
if ( nodeID.match( /\d+/ ) ) {
|
|
2041
|
+
|
|
2042
|
+
var animationNode = parseAnimationNode( FBXTree, rawNodes[ nodeID ], connections, sceneGraph );
|
|
2043
|
+
animationCurveNodes.push( animationNode );
|
|
2044
|
+
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
/**
|
|
2050
|
+
* @type {Map.<number, {
|
|
2051
|
+
id: number,
|
|
2052
|
+
attr: string,
|
|
2053
|
+
internalID: number,
|
|
2054
|
+
attrX: boolean,
|
|
2055
|
+
attrY: boolean,
|
|
2056
|
+
attrZ: boolean,
|
|
2057
|
+
containerBoneID: number,
|
|
2058
|
+
containerID: number,
|
|
2059
|
+
curves: {
|
|
2060
|
+
x: {
|
|
2061
|
+
version: any,
|
|
2062
|
+
id: number,
|
|
2063
|
+
internalID: number,
|
|
2064
|
+
times: number[],
|
|
2065
|
+
values: number[],
|
|
2066
|
+
attrFlag: number[],
|
|
2067
|
+
attrData: number[],
|
|
2068
|
+
},
|
|
2069
|
+
y: {
|
|
2070
|
+
version: any,
|
|
2071
|
+
id: number,
|
|
2072
|
+
internalID: number,
|
|
2073
|
+
times: number[],
|
|
2074
|
+
values: number[],
|
|
2075
|
+
attrFlag: number[],
|
|
2076
|
+
attrData: number[],
|
|
2077
|
+
},
|
|
2078
|
+
z: {
|
|
2079
|
+
version: any,
|
|
2080
|
+
id: number,
|
|
2081
|
+
internalID: number,
|
|
2082
|
+
times: number[],
|
|
2083
|
+
values: number[],
|
|
2084
|
+
attrFlag: number[],
|
|
2085
|
+
attrData: number[],
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
}>}
|
|
2089
|
+
*/
|
|
2090
|
+
var tmpMap = new Map();
|
|
2091
|
+
for ( var animationCurveNodeIndex = 0; animationCurveNodeIndex < animationCurveNodes.length; ++ animationCurveNodeIndex ) {
|
|
2092
|
+
|
|
2093
|
+
if ( animationCurveNodes[ animationCurveNodeIndex ] === null ) {
|
|
2094
|
+
|
|
2095
|
+
continue;
|
|
2096
|
+
|
|
2097
|
+
}
|
|
2098
|
+
tmpMap.set( animationCurveNodes[ animationCurveNodeIndex ].id, animationCurveNodes[ animationCurveNodeIndex ] );
|
|
2099
|
+
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
|
|
2103
|
+
/**
|
|
2104
|
+
* @type {{
|
|
2105
|
+
version: any,
|
|
2106
|
+
id: number,
|
|
2107
|
+
internalID: number,
|
|
2108
|
+
times: number[],
|
|
2109
|
+
values: number[],
|
|
2110
|
+
attrFlag: number[],
|
|
2111
|
+
attrData: number[],
|
|
2112
|
+
}[]}
|
|
2113
|
+
*/
|
|
2114
|
+
var animationCurves = [];
|
|
2115
|
+
for ( nodeID in rawCurves ) {
|
|
2116
|
+
|
|
2117
|
+
if ( nodeID.match( /\d+/ ) ) {
|
|
2118
|
+
|
|
2119
|
+
var animationCurve = parseAnimationCurve( rawCurves[ nodeID ] );
|
|
2120
|
+
|
|
2121
|
+
// seems like this check would be necessary?
|
|
2122
|
+
if ( ! connections.has( animationCurve.id ) ) continue;
|
|
2123
|
+
|
|
2124
|
+
animationCurves.push( animationCurve );
|
|
2125
|
+
|
|
2126
|
+
var firstParentConn = connections.get( animationCurve.id ).parents[ 0 ];
|
|
2127
|
+
var firstParentID = firstParentConn.ID;
|
|
2128
|
+
var firstParentRelationship = firstParentConn.relationship;
|
|
2129
|
+
var axis = '';
|
|
2130
|
+
|
|
2131
|
+
if ( firstParentRelationship.match( /X/ ) ) {
|
|
2132
|
+
|
|
2133
|
+
axis = 'x';
|
|
2134
|
+
|
|
2135
|
+
} else if ( firstParentRelationship.match( /Y/ ) ) {
|
|
2136
|
+
|
|
2137
|
+
axis = 'y';
|
|
2138
|
+
|
|
2139
|
+
} else if ( firstParentRelationship.match( /Z/ ) ) {
|
|
2140
|
+
|
|
2141
|
+
axis = 'z';
|
|
2142
|
+
|
|
2143
|
+
} else {
|
|
2144
|
+
|
|
2145
|
+
continue;
|
|
2146
|
+
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
tmpMap.get( firstParentID ).curves[ axis ] = animationCurve;
|
|
2150
|
+
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
tmpMap.forEach( function ( curveNode ) {
|
|
2156
|
+
|
|
2157
|
+
var id = curveNode.containerBoneID;
|
|
2158
|
+
if ( ! returnObject.curves.has( id ) ) {
|
|
2159
|
+
|
|
2160
|
+
returnObject.curves.set( id, { T: null, R: null, S: null } );
|
|
2161
|
+
|
|
2162
|
+
}
|
|
2163
|
+
returnObject.curves.get( id )[ curveNode.attr ] = curveNode;
|
|
2164
|
+
if ( curveNode.attr === 'R' ) {
|
|
2165
|
+
|
|
2166
|
+
var curves = curveNode.curves;
|
|
2167
|
+
if (!(curves.x && curves.y && curves.z)) return;
|
|
2168
|
+
|
|
2169
|
+
curves.x.values = curves.x.values.map( degreeToRadian );
|
|
2170
|
+
curves.y.values = curves.y.values.map( degreeToRadian );
|
|
2171
|
+
curves.z.values = curves.z.values.map( degreeToRadian );
|
|
2172
|
+
|
|
2173
|
+
if ( curveNode.preRotations !== null ) {
|
|
2174
|
+
|
|
2175
|
+
var preRotations = new THREE.Euler().setFromVector3( curveNode.preRotations, 'ZYX' );
|
|
2176
|
+
preRotations = new THREE.Quaternion().setFromEuler( preRotations );
|
|
2177
|
+
var frameRotation = new THREE.Euler();
|
|
2178
|
+
var frameRotationQuaternion = new THREE.Quaternion();
|
|
2179
|
+
for ( var frame = 0; frame < curves.x.times.length; ++ frame ) {
|
|
2180
|
+
|
|
2181
|
+
frameRotation.set( curves.x.values[ frame ], curves.y.values[ frame ], curves.z.values[ frame ], 'ZYX' );
|
|
2182
|
+
frameRotationQuaternion.setFromEuler( frameRotation ).premultiply( preRotations );
|
|
2183
|
+
frameRotation.setFromQuaternion( frameRotationQuaternion, 'ZYX' );
|
|
2184
|
+
curves.x.values[ frame ] = frameRotation.x;
|
|
2185
|
+
curves.y.values[ frame ] = frameRotation.y;
|
|
2186
|
+
curves.z.values[ frame ] = frameRotation.z;
|
|
2187
|
+
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
} );
|
|
2195
|
+
|
|
2196
|
+
for ( var nodeID in rawLayers ) {
|
|
2197
|
+
|
|
2198
|
+
/**
|
|
2199
|
+
* @type {{
|
|
2200
|
+
T: {
|
|
2201
|
+
id: number;
|
|
2202
|
+
attr: string;
|
|
2203
|
+
internalID: number;
|
|
2204
|
+
attrX: boolean;
|
|
2205
|
+
attrY: boolean;
|
|
2206
|
+
attrZ: boolean;
|
|
2207
|
+
containerBoneID: number;
|
|
2208
|
+
containerID: number;
|
|
2209
|
+
curves: {
|
|
2210
|
+
x: {
|
|
2211
|
+
version: any;
|
|
2212
|
+
id: number;
|
|
2213
|
+
internalID: number;
|
|
2214
|
+
times: number[];
|
|
2215
|
+
values: number[];
|
|
2216
|
+
attrFlag: number[];
|
|
2217
|
+
attrData: number[];
|
|
2218
|
+
};
|
|
2219
|
+
y: {
|
|
2220
|
+
version: any;
|
|
2221
|
+
id: number;
|
|
2222
|
+
internalID: number;
|
|
2223
|
+
times: number[];
|
|
2224
|
+
values: number[];
|
|
2225
|
+
attrFlag: number[];
|
|
2226
|
+
attrData: number[];
|
|
2227
|
+
};
|
|
2228
|
+
z: {
|
|
2229
|
+
version: any;
|
|
2230
|
+
id: number;
|
|
2231
|
+
internalID: number;
|
|
2232
|
+
times: number[];
|
|
2233
|
+
values: number[];
|
|
2234
|
+
attrFlag: number[];
|
|
2235
|
+
attrData: number[];
|
|
2236
|
+
};
|
|
2237
|
+
},
|
|
2238
|
+
},
|
|
2239
|
+
R: {
|
|
2240
|
+
id: number;
|
|
2241
|
+
attr: string;
|
|
2242
|
+
internalID: number;
|
|
2243
|
+
attrX: boolean;
|
|
2244
|
+
attrY: boolean;
|
|
2245
|
+
attrZ: boolean;
|
|
2246
|
+
containerBoneID: number;
|
|
2247
|
+
containerID: number;
|
|
2248
|
+
curves: {
|
|
2249
|
+
x: {
|
|
2250
|
+
version: any;
|
|
2251
|
+
id: number;
|
|
2252
|
+
internalID: number;
|
|
2253
|
+
times: number[];
|
|
2254
|
+
values: number[];
|
|
2255
|
+
attrFlag: number[];
|
|
2256
|
+
attrData: number[];
|
|
2257
|
+
};
|
|
2258
|
+
y: {
|
|
2259
|
+
version: any;
|
|
2260
|
+
id: number;
|
|
2261
|
+
internalID: number;
|
|
2262
|
+
times: number[];
|
|
2263
|
+
values: number[];
|
|
2264
|
+
attrFlag: number[];
|
|
2265
|
+
attrData: number[];
|
|
2266
|
+
};
|
|
2267
|
+
z: {
|
|
2268
|
+
version: any;
|
|
2269
|
+
id: number;
|
|
2270
|
+
internalID: number;
|
|
2271
|
+
times: number[];
|
|
2272
|
+
values: number[];
|
|
2273
|
+
attrFlag: number[];
|
|
2274
|
+
attrData: number[];
|
|
2275
|
+
};
|
|
2276
|
+
},
|
|
2277
|
+
},
|
|
2278
|
+
S: {
|
|
2279
|
+
id: number;
|
|
2280
|
+
attr: string;
|
|
2281
|
+
internalID: number;
|
|
2282
|
+
attrX: boolean;
|
|
2283
|
+
attrY: boolean;
|
|
2284
|
+
attrZ: boolean;
|
|
2285
|
+
containerBoneID: number;
|
|
2286
|
+
containerID: number;
|
|
2287
|
+
curves: {
|
|
2288
|
+
x: {
|
|
2289
|
+
version: any;
|
|
2290
|
+
id: number;
|
|
2291
|
+
internalID: number;
|
|
2292
|
+
times: number[];
|
|
2293
|
+
values: number[];
|
|
2294
|
+
attrFlag: number[];
|
|
2295
|
+
attrData: number[];
|
|
2296
|
+
};
|
|
2297
|
+
y: {
|
|
2298
|
+
version: any;
|
|
2299
|
+
id: number;
|
|
2300
|
+
internalID: number;
|
|
2301
|
+
times: number[];
|
|
2302
|
+
values: number[];
|
|
2303
|
+
attrFlag: number[];
|
|
2304
|
+
attrData: number[];
|
|
2305
|
+
};
|
|
2306
|
+
z: {
|
|
2307
|
+
version: any;
|
|
2308
|
+
id: number;
|
|
2309
|
+
internalID: number;
|
|
2310
|
+
times: number[];
|
|
2311
|
+
values: number[];
|
|
2312
|
+
attrFlag: number[];
|
|
2313
|
+
attrData: number[];
|
|
2314
|
+
};
|
|
2315
|
+
},
|
|
2316
|
+
}
|
|
2317
|
+
}[]}
|
|
2318
|
+
*/
|
|
2319
|
+
var layer = [];
|
|
2320
|
+
var children = connections.get( parseInt( nodeID ) ).children;
|
|
2321
|
+
|
|
2322
|
+
for ( var childIndex = 0; childIndex < children.length; childIndex ++ ) {
|
|
2323
|
+
|
|
2324
|
+
// Skip lockInfluenceWeights
|
|
2325
|
+
if ( tmpMap.has( children[ childIndex ].ID ) ) {
|
|
2326
|
+
|
|
2327
|
+
var curveNode = tmpMap.get( children[ childIndex ].ID );
|
|
2328
|
+
var boneID = curveNode.containerBoneID;
|
|
2329
|
+
if ( layer[ boneID ] === undefined ) {
|
|
2330
|
+
|
|
2331
|
+
layer[ boneID ] = {
|
|
2332
|
+
T: null,
|
|
2333
|
+
R: null,
|
|
2334
|
+
S: null
|
|
2335
|
+
};
|
|
2336
|
+
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
layer[ boneID ][ curveNode.attr ] = curveNode;
|
|
2340
|
+
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
returnObject.layers[ nodeID ] = layer;
|
|
2346
|
+
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
for ( var nodeID in rawStacks ) {
|
|
2350
|
+
|
|
2351
|
+
var layers = [];
|
|
2352
|
+
var children = connections.get( parseInt( nodeID ) ).children;
|
|
2353
|
+
var timestamps = { max: 0, min: Number.MAX_VALUE };
|
|
2354
|
+
|
|
2355
|
+
for ( var childIndex = 0; childIndex < children.length; ++ childIndex ) {
|
|
2356
|
+
|
|
2357
|
+
var currentLayer = returnObject.layers[ children[ childIndex ].ID ];
|
|
2358
|
+
|
|
2359
|
+
if ( currentLayer !== undefined ) {
|
|
2360
|
+
|
|
2361
|
+
layers.push( currentLayer );
|
|
2362
|
+
|
|
2363
|
+
for ( var currentLayerIndex = 0, currentLayerLength = currentLayer.length; currentLayerIndex < currentLayerLength; ++ currentLayerIndex ) {
|
|
2364
|
+
|
|
2365
|
+
var layer = currentLayer[ currentLayerIndex ];
|
|
2366
|
+
|
|
2367
|
+
if ( layer ) {
|
|
2368
|
+
|
|
2369
|
+
getCurveNodeMaxMinTimeStamps( layer, timestamps );
|
|
2370
|
+
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
// Do we have an animation clip with actual length?
|
|
2380
|
+
if ( timestamps.max > timestamps.min ) {
|
|
2381
|
+
|
|
2382
|
+
returnObject.stacks[ nodeID ] = {
|
|
2383
|
+
name: rawStacks[ nodeID ].attrName,
|
|
2384
|
+
layers: layers,
|
|
2385
|
+
length: timestamps.max - timestamps.min,
|
|
2386
|
+
frames: ( timestamps.max - timestamps.min ) * 30
|
|
2387
|
+
};
|
|
2388
|
+
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
return returnObject;
|
|
2394
|
+
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
/**
|
|
2398
|
+
* @param {Object} FBXTree
|
|
2399
|
+
* @param {{id: number, attrName: string, properties: Object<string, any>}} animationCurveNode
|
|
2400
|
+
* @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
|
|
2401
|
+
* @param {{skeleton: {bones: {FBX_ID: number}[]}}} sceneGraph
|
|
2402
|
+
*/
|
|
2403
|
+
function parseAnimationNode( FBXTree, animationCurveNode, connections, sceneGraph ) {
|
|
2404
|
+
|
|
2405
|
+
var rawModels = FBXTree.Objects.subNodes.Model;
|
|
2406
|
+
|
|
2407
|
+
var returnObject = {
|
|
2408
|
+
/**
|
|
2409
|
+
* @type {number}
|
|
2410
|
+
*/
|
|
2411
|
+
id: animationCurveNode.id,
|
|
2412
|
+
|
|
2413
|
+
/**
|
|
2414
|
+
* @type {string}
|
|
2415
|
+
*/
|
|
2416
|
+
attr: animationCurveNode.attrName,
|
|
2417
|
+
|
|
2418
|
+
/**
|
|
2419
|
+
* @type {number}
|
|
2420
|
+
*/
|
|
2421
|
+
internalID: animationCurveNode.id,
|
|
2422
|
+
|
|
2423
|
+
/**
|
|
2424
|
+
* @type {boolean}
|
|
2425
|
+
*/
|
|
2426
|
+
attrX: false,
|
|
2427
|
+
|
|
2428
|
+
/**
|
|
2429
|
+
* @type {boolean}
|
|
2430
|
+
*/
|
|
2431
|
+
attrY: false,
|
|
2432
|
+
|
|
2433
|
+
/**
|
|
2434
|
+
* @type {boolean}
|
|
2435
|
+
*/
|
|
2436
|
+
attrZ: false,
|
|
2437
|
+
|
|
2438
|
+
/**
|
|
2439
|
+
* @type {number}
|
|
2440
|
+
*/
|
|
2441
|
+
containerBoneID: - 1,
|
|
2442
|
+
|
|
2443
|
+
/**
|
|
2444
|
+
* @type {number}
|
|
2445
|
+
*/
|
|
2446
|
+
containerID: - 1,
|
|
2447
|
+
|
|
2448
|
+
curves: {
|
|
2449
|
+
x: null,
|
|
2450
|
+
y: null,
|
|
2451
|
+
z: null
|
|
2452
|
+
},
|
|
2453
|
+
|
|
2454
|
+
/**
|
|
2455
|
+
* @type {number[]}
|
|
2456
|
+
*/
|
|
2457
|
+
preRotations: null
|
|
2458
|
+
};
|
|
2459
|
+
|
|
2460
|
+
if ( returnObject.attr.match( /S|R|T/ ) ) {
|
|
2461
|
+
|
|
2462
|
+
for ( var attributeKey in animationCurveNode.properties ) {
|
|
2463
|
+
|
|
2464
|
+
if ( attributeKey.match( /X/ ) ) {
|
|
2465
|
+
|
|
2466
|
+
returnObject.attrX = true;
|
|
2467
|
+
|
|
2468
|
+
}
|
|
2469
|
+
if ( attributeKey.match( /Y/ ) ) {
|
|
2470
|
+
|
|
2471
|
+
returnObject.attrY = true;
|
|
2472
|
+
|
|
2473
|
+
}
|
|
2474
|
+
if ( attributeKey.match( /Z/ ) ) {
|
|
2475
|
+
|
|
2476
|
+
returnObject.attrZ = true;
|
|
2477
|
+
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
} else {
|
|
2483
|
+
|
|
2484
|
+
return null;
|
|
2485
|
+
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
var conns = connections.get( returnObject.id );
|
|
2489
|
+
var containerIndices = conns.parents;
|
|
2490
|
+
|
|
2491
|
+
for ( var containerIndicesIndex = containerIndices.length - 1; containerIndicesIndex >= 0; -- containerIndicesIndex ) {
|
|
2492
|
+
|
|
2493
|
+
var boneID = findIndex( sceneGraph.skeleton.bones, function ( bone ) {
|
|
2494
|
+
|
|
2495
|
+
return bone.FBX_ID === containerIndices[ containerIndicesIndex ].ID;
|
|
2496
|
+
|
|
2497
|
+
} );
|
|
2498
|
+
if ( boneID > - 1 ) {
|
|
2499
|
+
|
|
2500
|
+
returnObject.containerBoneID = boneID;
|
|
2501
|
+
returnObject.containerID = containerIndices[ containerIndicesIndex ].ID;
|
|
2502
|
+
var model = rawModels[ returnObject.containerID.toString() ];
|
|
2503
|
+
if ( 'PreRotation' in model.properties ) {
|
|
2504
|
+
|
|
2505
|
+
returnObject.preRotations = parseVector3( model.properties.PreRotation ).multiplyScalar( Math.PI / 180 );
|
|
2506
|
+
|
|
2507
|
+
}
|
|
2508
|
+
break;
|
|
2509
|
+
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
return returnObject;
|
|
2515
|
+
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
/**
|
|
2519
|
+
* @param {{id: number, subNodes: {KeyTime: {properties: {a: string}}, KeyValueFloat: {properties: {a: string}}, KeyAttrFlags: {properties: {a: string}}, KeyAttrDataFloat: {properties: {a: string}}}}} animationCurve
|
|
2520
|
+
*/
|
|
2521
|
+
function parseAnimationCurve( animationCurve ) {
|
|
2522
|
+
|
|
2523
|
+
return {
|
|
2524
|
+
version: null,
|
|
2525
|
+
id: animationCurve.id,
|
|
2526
|
+
internalID: animationCurve.id,
|
|
2527
|
+
times: parseFloatArray( animationCurve.subNodes.KeyTime.properties.a ).map( convertFBXTimeToSeconds ),
|
|
2528
|
+
values: parseFloatArray( animationCurve.subNodes.KeyValueFloat.properties.a ),
|
|
2529
|
+
|
|
2530
|
+
attrFlag: parseIntArray( animationCurve.subNodes.KeyAttrFlags.properties.a ),
|
|
2531
|
+
attrData: parseFloatArray( animationCurve.subNodes.KeyAttrDataFloat.properties.a )
|
|
2532
|
+
};
|
|
2533
|
+
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
/**
|
|
2537
|
+
* Sets the maxTimeStamp and minTimeStamp variables if it has timeStamps that are either larger or smaller
|
|
2538
|
+
* than the max or min respectively.
|
|
2539
|
+
* @param {{
|
|
2540
|
+
T: {
|
|
2541
|
+
id: number,
|
|
2542
|
+
attr: string,
|
|
2543
|
+
internalID: number,
|
|
2544
|
+
attrX: boolean,
|
|
2545
|
+
attrY: boolean,
|
|
2546
|
+
attrZ: boolean,
|
|
2547
|
+
containerBoneID: number,
|
|
2548
|
+
containerID: number,
|
|
2549
|
+
curves: {
|
|
2550
|
+
x: {
|
|
2551
|
+
version: any,
|
|
2552
|
+
id: number,
|
|
2553
|
+
internalID: number,
|
|
2554
|
+
times: number[],
|
|
2555
|
+
values: number[],
|
|
2556
|
+
attrFlag: number[],
|
|
2557
|
+
attrData: number[],
|
|
2558
|
+
},
|
|
2559
|
+
y: {
|
|
2560
|
+
version: any,
|
|
2561
|
+
id: number,
|
|
2562
|
+
internalID: number,
|
|
2563
|
+
times: number[],
|
|
2564
|
+
values: number[],
|
|
2565
|
+
attrFlag: number[],
|
|
2566
|
+
attrData: number[],
|
|
2567
|
+
},
|
|
2568
|
+
z: {
|
|
2569
|
+
version: any,
|
|
2570
|
+
id: number,
|
|
2571
|
+
internalID: number,
|
|
2572
|
+
times: number[],
|
|
2573
|
+
values: number[],
|
|
2574
|
+
attrFlag: number[],
|
|
2575
|
+
attrData: number[],
|
|
2576
|
+
},
|
|
2577
|
+
},
|
|
2578
|
+
},
|
|
2579
|
+
R: {
|
|
2580
|
+
id: number,
|
|
2581
|
+
attr: string,
|
|
2582
|
+
internalID: number,
|
|
2583
|
+
attrX: boolean,
|
|
2584
|
+
attrY: boolean,
|
|
2585
|
+
attrZ: boolean,
|
|
2586
|
+
containerBoneID: number,
|
|
2587
|
+
containerID: number,
|
|
2588
|
+
curves: {
|
|
2589
|
+
x: {
|
|
2590
|
+
version: any,
|
|
2591
|
+
id: number,
|
|
2592
|
+
internalID: number,
|
|
2593
|
+
times: number[],
|
|
2594
|
+
values: number[],
|
|
2595
|
+
attrFlag: number[],
|
|
2596
|
+
attrData: number[],
|
|
2597
|
+
},
|
|
2598
|
+
y: {
|
|
2599
|
+
version: any,
|
|
2600
|
+
id: number,
|
|
2601
|
+
internalID: number,
|
|
2602
|
+
times: number[],
|
|
2603
|
+
values: number[],
|
|
2604
|
+
attrFlag: number[],
|
|
2605
|
+
attrData: number[],
|
|
2606
|
+
},
|
|
2607
|
+
z: {
|
|
2608
|
+
version: any,
|
|
2609
|
+
id: number,
|
|
2610
|
+
internalID: number,
|
|
2611
|
+
times: number[],
|
|
2612
|
+
values: number[],
|
|
2613
|
+
attrFlag: number[],
|
|
2614
|
+
attrData: number[],
|
|
2615
|
+
},
|
|
2616
|
+
},
|
|
2617
|
+
},
|
|
2618
|
+
S: {
|
|
2619
|
+
id: number,
|
|
2620
|
+
attr: string,
|
|
2621
|
+
internalID: number,
|
|
2622
|
+
attrX: boolean,
|
|
2623
|
+
attrY: boolean,
|
|
2624
|
+
attrZ: boolean,
|
|
2625
|
+
containerBoneID: number,
|
|
2626
|
+
containerID: number,
|
|
2627
|
+
curves: {
|
|
2628
|
+
x: {
|
|
2629
|
+
version: any,
|
|
2630
|
+
id: number,
|
|
2631
|
+
internalID: number,
|
|
2632
|
+
times: number[],
|
|
2633
|
+
values: number[],
|
|
2634
|
+
attrFlag: number[],
|
|
2635
|
+
attrData: number[],
|
|
2636
|
+
},
|
|
2637
|
+
y: {
|
|
2638
|
+
version: any,
|
|
2639
|
+
id: number,
|
|
2640
|
+
internalID: number,
|
|
2641
|
+
times: number[],
|
|
2642
|
+
values: number[],
|
|
2643
|
+
attrFlag: number[],
|
|
2644
|
+
attrData: number[],
|
|
2645
|
+
},
|
|
2646
|
+
z: {
|
|
2647
|
+
version: any,
|
|
2648
|
+
id: number,
|
|
2649
|
+
internalID: number,
|
|
2650
|
+
times: number[],
|
|
2651
|
+
values: number[],
|
|
2652
|
+
attrFlag: number[],
|
|
2653
|
+
attrData: number[],
|
|
2654
|
+
},
|
|
2655
|
+
},
|
|
2656
|
+
},
|
|
2657
|
+
}} layer
|
|
2658
|
+
*/
|
|
2659
|
+
function getCurveNodeMaxMinTimeStamps( layer, timestamps ) {
|
|
2660
|
+
|
|
2661
|
+
if ( layer.R ) {
|
|
2662
|
+
|
|
2663
|
+
getCurveMaxMinTimeStamp( layer.R.curves, timestamps );
|
|
2664
|
+
|
|
2665
|
+
}
|
|
2666
|
+
if ( layer.S ) {
|
|
2667
|
+
|
|
2668
|
+
getCurveMaxMinTimeStamp( layer.S.curves, timestamps );
|
|
2669
|
+
|
|
2670
|
+
}
|
|
2671
|
+
if ( layer.T ) {
|
|
2672
|
+
|
|
2673
|
+
getCurveMaxMinTimeStamp( layer.T.curves, timestamps );
|
|
2674
|
+
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
/**
|
|
2680
|
+
* Sets the maxTimeStamp and minTimeStamp if one of the curve's time stamps
|
|
2681
|
+
* exceeds the maximum or minimum.
|
|
2682
|
+
* @param {{
|
|
2683
|
+
x: {
|
|
2684
|
+
version: any,
|
|
2685
|
+
id: number,
|
|
2686
|
+
internalID: number,
|
|
2687
|
+
times: number[],
|
|
2688
|
+
values: number[],
|
|
2689
|
+
attrFlag: number[],
|
|
2690
|
+
attrData: number[],
|
|
2691
|
+
},
|
|
2692
|
+
y: {
|
|
2693
|
+
version: any,
|
|
2694
|
+
id: number,
|
|
2695
|
+
internalID: number,
|
|
2696
|
+
times: number[],
|
|
2697
|
+
values: number[],
|
|
2698
|
+
attrFlag: number[],
|
|
2699
|
+
attrData: number[],
|
|
2700
|
+
},
|
|
2701
|
+
z: {
|
|
2702
|
+
version: any,
|
|
2703
|
+
id: number,
|
|
2704
|
+
internalID: number,
|
|
2705
|
+
times: number[],
|
|
2706
|
+
values: number[],
|
|
2707
|
+
attrFlag: number[],
|
|
2708
|
+
attrData: number[],
|
|
2709
|
+
}
|
|
2710
|
+
}} curve
|
|
2711
|
+
*/
|
|
2712
|
+
function getCurveMaxMinTimeStamp( curve, timestamps ) {
|
|
2713
|
+
|
|
2714
|
+
if ( curve.x ) {
|
|
2715
|
+
|
|
2716
|
+
getCurveAxisMaxMinTimeStamps( curve.x, timestamps );
|
|
2717
|
+
|
|
2718
|
+
}
|
|
2719
|
+
if ( curve.y ) {
|
|
2720
|
+
|
|
2721
|
+
getCurveAxisMaxMinTimeStamps( curve.y, timestamps );
|
|
2722
|
+
|
|
2723
|
+
}
|
|
2724
|
+
if ( curve.z ) {
|
|
2725
|
+
|
|
2726
|
+
getCurveAxisMaxMinTimeStamps( curve.z, timestamps );
|
|
2727
|
+
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
}
|
|
2731
|
+
|
|
2732
|
+
/**
|
|
2733
|
+
* Sets the maxTimeStamp and minTimeStamp if one of its timestamps exceeds the maximum or minimum.
|
|
2734
|
+
* @param {{times: number[]}} axis
|
|
2735
|
+
*/
|
|
2736
|
+
function getCurveAxisMaxMinTimeStamps( axis, timestamps ) {
|
|
2737
|
+
|
|
2738
|
+
timestamps.max = axis.times[ axis.times.length - 1 ] > timestamps.max ? axis.times[ axis.times.length - 1 ] : timestamps.max;
|
|
2739
|
+
timestamps.min = axis.times[ 0 ] < timestamps.min ? axis.times[ 0 ] : timestamps.min;
|
|
2740
|
+
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
/**
|
|
2744
|
+
* @param {{
|
|
2745
|
+
curves: Map<number, {
|
|
2746
|
+
T: {
|
|
2747
|
+
id: number;
|
|
2748
|
+
attr: string;
|
|
2749
|
+
internalID: number;
|
|
2750
|
+
attrX: boolean;
|
|
2751
|
+
attrY: boolean;
|
|
2752
|
+
attrZ: boolean;
|
|
2753
|
+
containerBoneID: number;
|
|
2754
|
+
containerID: number;
|
|
2755
|
+
curves: {
|
|
2756
|
+
x: {
|
|
2757
|
+
version: any;
|
|
2758
|
+
id: number;
|
|
2759
|
+
internalID: number;
|
|
2760
|
+
times: number[];
|
|
2761
|
+
values: number[];
|
|
2762
|
+
attrFlag: number[];
|
|
2763
|
+
attrData: number[];
|
|
2764
|
+
};
|
|
2765
|
+
y: {
|
|
2766
|
+
version: any;
|
|
2767
|
+
id: number;
|
|
2768
|
+
internalID: number;
|
|
2769
|
+
times: number[];
|
|
2770
|
+
values: number[];
|
|
2771
|
+
attrFlag: number[];
|
|
2772
|
+
attrData: number[];
|
|
2773
|
+
};
|
|
2774
|
+
z: {
|
|
2775
|
+
version: any;
|
|
2776
|
+
id: number;
|
|
2777
|
+
internalID: number;
|
|
2778
|
+
times: number[];
|
|
2779
|
+
values: number[];
|
|
2780
|
+
attrFlag: number[];
|
|
2781
|
+
attrData: number[];
|
|
2782
|
+
};
|
|
2783
|
+
};
|
|
2784
|
+
};
|
|
2785
|
+
R: {
|
|
2786
|
+
id: number;
|
|
2787
|
+
attr: string;
|
|
2788
|
+
internalID: number;
|
|
2789
|
+
attrX: boolean;
|
|
2790
|
+
attrY: boolean;
|
|
2791
|
+
attrZ: boolean;
|
|
2792
|
+
containerBoneID: number;
|
|
2793
|
+
containerID: number;
|
|
2794
|
+
curves: {
|
|
2795
|
+
x: {
|
|
2796
|
+
version: any;
|
|
2797
|
+
id: number;
|
|
2798
|
+
internalID: number;
|
|
2799
|
+
times: number[];
|
|
2800
|
+
values: number[];
|
|
2801
|
+
attrFlag: number[];
|
|
2802
|
+
attrData: number[];
|
|
2803
|
+
};
|
|
2804
|
+
y: {
|
|
2805
|
+
version: any;
|
|
2806
|
+
id: number;
|
|
2807
|
+
internalID: number;
|
|
2808
|
+
times: number[];
|
|
2809
|
+
values: number[];
|
|
2810
|
+
attrFlag: number[];
|
|
2811
|
+
attrData: number[];
|
|
2812
|
+
};
|
|
2813
|
+
z: {
|
|
2814
|
+
version: any;
|
|
2815
|
+
id: number;
|
|
2816
|
+
internalID: number;
|
|
2817
|
+
times: number[];
|
|
2818
|
+
values: number[];
|
|
2819
|
+
attrFlag: number[];
|
|
2820
|
+
attrData: number[];
|
|
2821
|
+
};
|
|
2822
|
+
};
|
|
2823
|
+
};
|
|
2824
|
+
S: {
|
|
2825
|
+
id: number;
|
|
2826
|
+
attr: string;
|
|
2827
|
+
internalID: number;
|
|
2828
|
+
attrX: boolean;
|
|
2829
|
+
attrY: boolean;
|
|
2830
|
+
attrZ: boolean;
|
|
2831
|
+
containerBoneID: number;
|
|
2832
|
+
containerID: number;
|
|
2833
|
+
curves: {
|
|
2834
|
+
x: {
|
|
2835
|
+
version: any;
|
|
2836
|
+
id: number;
|
|
2837
|
+
internalID: number;
|
|
2838
|
+
times: number[];
|
|
2839
|
+
values: number[];
|
|
2840
|
+
attrFlag: number[];
|
|
2841
|
+
attrData: number[];
|
|
2842
|
+
};
|
|
2843
|
+
y: {
|
|
2844
|
+
version: any;
|
|
2845
|
+
id: number;
|
|
2846
|
+
internalID: number;
|
|
2847
|
+
times: number[];
|
|
2848
|
+
values: number[];
|
|
2849
|
+
attrFlag: number[];
|
|
2850
|
+
attrData: number[];
|
|
2851
|
+
};
|
|
2852
|
+
z: {
|
|
2853
|
+
version: any;
|
|
2854
|
+
id: number;
|
|
2855
|
+
internalID: number;
|
|
2856
|
+
times: number[];
|
|
2857
|
+
values: number[];
|
|
2858
|
+
attrFlag: number[];
|
|
2859
|
+
attrData: number[];
|
|
2860
|
+
};
|
|
2861
|
+
};
|
|
2862
|
+
};
|
|
2863
|
+
}>;
|
|
2864
|
+
layers: Map<number, {
|
|
2865
|
+
T: {
|
|
2866
|
+
id: number;
|
|
2867
|
+
attr: string;
|
|
2868
|
+
internalID: number;
|
|
2869
|
+
attrX: boolean;
|
|
2870
|
+
attrY: boolean;
|
|
2871
|
+
attrZ: boolean;
|
|
2872
|
+
containerBoneID: number;
|
|
2873
|
+
containerID: number;
|
|
2874
|
+
curves: {
|
|
2875
|
+
x: {
|
|
2876
|
+
version: any;
|
|
2877
|
+
id: number;
|
|
2878
|
+
internalID: number;
|
|
2879
|
+
times: number[];
|
|
2880
|
+
values: number[];
|
|
2881
|
+
attrFlag: number[];
|
|
2882
|
+
attrData: number[];
|
|
2883
|
+
};
|
|
2884
|
+
y: {
|
|
2885
|
+
version: any;
|
|
2886
|
+
id: number;
|
|
2887
|
+
internalID: number;
|
|
2888
|
+
times: number[];
|
|
2889
|
+
values: number[];
|
|
2890
|
+
attrFlag: number[];
|
|
2891
|
+
attrData: number[];
|
|
2892
|
+
};
|
|
2893
|
+
z: {
|
|
2894
|
+
version: any;
|
|
2895
|
+
id: number;
|
|
2896
|
+
internalID: number;
|
|
2897
|
+
times: number[];
|
|
2898
|
+
values: number[];
|
|
2899
|
+
attrFlag: number[];
|
|
2900
|
+
attrData: number[];
|
|
2901
|
+
};
|
|
2902
|
+
};
|
|
2903
|
+
};
|
|
2904
|
+
R: {
|
|
2905
|
+
id: number;
|
|
2906
|
+
attr: string;
|
|
2907
|
+
internalID: number;
|
|
2908
|
+
attrX: boolean;
|
|
2909
|
+
attrY: boolean;
|
|
2910
|
+
attrZ: boolean;
|
|
2911
|
+
containerBoneID: number;
|
|
2912
|
+
containerID: number;
|
|
2913
|
+
curves: {
|
|
2914
|
+
x: {
|
|
2915
|
+
version: any;
|
|
2916
|
+
id: number;
|
|
2917
|
+
internalID: number;
|
|
2918
|
+
times: number[];
|
|
2919
|
+
values: number[];
|
|
2920
|
+
attrFlag: number[];
|
|
2921
|
+
attrData: number[];
|
|
2922
|
+
};
|
|
2923
|
+
y: {
|
|
2924
|
+
version: any;
|
|
2925
|
+
id: number;
|
|
2926
|
+
internalID: number;
|
|
2927
|
+
times: number[];
|
|
2928
|
+
values: number[];
|
|
2929
|
+
attrFlag: number[];
|
|
2930
|
+
attrData: number[];
|
|
2931
|
+
};
|
|
2932
|
+
z: {
|
|
2933
|
+
version: any;
|
|
2934
|
+
id: number;
|
|
2935
|
+
internalID: number;
|
|
2936
|
+
times: number[];
|
|
2937
|
+
values: number[];
|
|
2938
|
+
attrFlag: number[];
|
|
2939
|
+
attrData: number[];
|
|
2940
|
+
};
|
|
2941
|
+
};
|
|
2942
|
+
};
|
|
2943
|
+
S: {
|
|
2944
|
+
id: number;
|
|
2945
|
+
attr: string;
|
|
2946
|
+
internalID: number;
|
|
2947
|
+
attrX: boolean;
|
|
2948
|
+
attrY: boolean;
|
|
2949
|
+
attrZ: boolean;
|
|
2950
|
+
containerBoneID: number;
|
|
2951
|
+
containerID: number;
|
|
2952
|
+
curves: {
|
|
2953
|
+
x: {
|
|
2954
|
+
version: any;
|
|
2955
|
+
id: number;
|
|
2956
|
+
internalID: number;
|
|
2957
|
+
times: number[];
|
|
2958
|
+
values: number[];
|
|
2959
|
+
attrFlag: number[];
|
|
2960
|
+
attrData: number[];
|
|
2961
|
+
};
|
|
2962
|
+
y: {
|
|
2963
|
+
version: any;
|
|
2964
|
+
id: number;
|
|
2965
|
+
internalID: number;
|
|
2966
|
+
times: number[];
|
|
2967
|
+
values: number[];
|
|
2968
|
+
attrFlag: number[];
|
|
2969
|
+
attrData: number[];
|
|
2970
|
+
};
|
|
2971
|
+
z: {
|
|
2972
|
+
version: any;
|
|
2973
|
+
id: number;
|
|
2974
|
+
internalID: number;
|
|
2975
|
+
times: number[];
|
|
2976
|
+
values: number[];
|
|
2977
|
+
attrFlag: number[];
|
|
2978
|
+
attrData: number[];
|
|
2979
|
+
};
|
|
2980
|
+
};
|
|
2981
|
+
};
|
|
2982
|
+
}[]>;
|
|
2983
|
+
stacks: Map<number, {
|
|
2984
|
+
name: string;
|
|
2985
|
+
layers: {
|
|
2986
|
+
T: {
|
|
2987
|
+
id: number;
|
|
2988
|
+
attr: string;
|
|
2989
|
+
internalID: number;
|
|
2990
|
+
attrX: boolean;
|
|
2991
|
+
attrY: boolean;
|
|
2992
|
+
attrZ: boolean;
|
|
2993
|
+
containerBoneID: number;
|
|
2994
|
+
containerID: number;
|
|
2995
|
+
curves: {
|
|
2996
|
+
x: {
|
|
2997
|
+
version: any;
|
|
2998
|
+
id: number;
|
|
2999
|
+
internalID: number;
|
|
3000
|
+
times: number[];
|
|
3001
|
+
values: number[];
|
|
3002
|
+
attrFlag: number[];
|
|
3003
|
+
attrData: number[];
|
|
3004
|
+
};
|
|
3005
|
+
y: {
|
|
3006
|
+
version: any;
|
|
3007
|
+
id: number;
|
|
3008
|
+
internalID: number;
|
|
3009
|
+
times: number[];
|
|
3010
|
+
values: number[];
|
|
3011
|
+
attrFlag: number[];
|
|
3012
|
+
attrData: number[];
|
|
3013
|
+
};
|
|
3014
|
+
z: {
|
|
3015
|
+
version: any;
|
|
3016
|
+
id: number;
|
|
3017
|
+
internalID: number;
|
|
3018
|
+
times: number[];
|
|
3019
|
+
values: number[];
|
|
3020
|
+
attrFlag: number[];
|
|
3021
|
+
attrData: number[];
|
|
3022
|
+
};
|
|
3023
|
+
};
|
|
3024
|
+
};
|
|
3025
|
+
R: {
|
|
3026
|
+
id: number;
|
|
3027
|
+
attr: string;
|
|
3028
|
+
internalID: number;
|
|
3029
|
+
attrX: boolean;
|
|
3030
|
+
attrY: boolean;
|
|
3031
|
+
attrZ: boolean;
|
|
3032
|
+
containerBoneID: number;
|
|
3033
|
+
containerID: number;
|
|
3034
|
+
curves: {
|
|
3035
|
+
x: {
|
|
3036
|
+
version: any;
|
|
3037
|
+
id: number;
|
|
3038
|
+
internalID: number;
|
|
3039
|
+
times: number[];
|
|
3040
|
+
values: number[];
|
|
3041
|
+
attrFlag: number[];
|
|
3042
|
+
attrData: number[];
|
|
3043
|
+
};
|
|
3044
|
+
y: {
|
|
3045
|
+
version: any;
|
|
3046
|
+
id: number;
|
|
3047
|
+
internalID: number;
|
|
3048
|
+
times: number[];
|
|
3049
|
+
values: number[];
|
|
3050
|
+
attrFlag: number[];
|
|
3051
|
+
attrData: number[];
|
|
3052
|
+
};
|
|
3053
|
+
z: {
|
|
3054
|
+
version: any;
|
|
3055
|
+
id: number;
|
|
3056
|
+
internalID: number;
|
|
3057
|
+
times: number[];
|
|
3058
|
+
values: number[];
|
|
3059
|
+
attrFlag: number[];
|
|
3060
|
+
attrData: number[];
|
|
3061
|
+
};
|
|
3062
|
+
};
|
|
3063
|
+
};
|
|
3064
|
+
S: {
|
|
3065
|
+
id: number;
|
|
3066
|
+
attr: string;
|
|
3067
|
+
internalID: number;
|
|
3068
|
+
attrX: boolean;
|
|
3069
|
+
attrY: boolean;
|
|
3070
|
+
attrZ: boolean;
|
|
3071
|
+
containerBoneID: number;
|
|
3072
|
+
containerID: number;
|
|
3073
|
+
curves: {
|
|
3074
|
+
x: {
|
|
3075
|
+
version: any;
|
|
3076
|
+
id: number;
|
|
3077
|
+
internalID: number;
|
|
3078
|
+
times: number[];
|
|
3079
|
+
values: number[];
|
|
3080
|
+
attrFlag: number[];
|
|
3081
|
+
attrData: number[];
|
|
3082
|
+
};
|
|
3083
|
+
y: {
|
|
3084
|
+
version: any;
|
|
3085
|
+
id: number;
|
|
3086
|
+
internalID: number;
|
|
3087
|
+
times: number[];
|
|
3088
|
+
values: number[];
|
|
3089
|
+
attrFlag: number[];
|
|
3090
|
+
attrData: number[];
|
|
3091
|
+
};
|
|
3092
|
+
z: {
|
|
3093
|
+
version: any;
|
|
3094
|
+
id: number;
|
|
3095
|
+
internalID: number;
|
|
3096
|
+
times: number[];
|
|
3097
|
+
values: number[];
|
|
3098
|
+
attrFlag: number[];
|
|
3099
|
+
attrData: number[];
|
|
3100
|
+
};
|
|
3101
|
+
};
|
|
3102
|
+
};
|
|
3103
|
+
}[][];
|
|
3104
|
+
length: number;
|
|
3105
|
+
frames: number;
|
|
3106
|
+
}>;
|
|
3107
|
+
length: number;
|
|
3108
|
+
fps: number;
|
|
3109
|
+
frames: number;
|
|
3110
|
+
}} animations,
|
|
3111
|
+
* @param {{skeleton: { bones: THREE.Bone[]}}} group
|
|
3112
|
+
*/
|
|
3113
|
+
function addAnimations( group, animations ) {
|
|
3114
|
+
|
|
3115
|
+
if ( group.animations === undefined ) {
|
|
3116
|
+
|
|
3117
|
+
group.animations = [];
|
|
3118
|
+
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
var stacks = animations.stacks;
|
|
3122
|
+
|
|
3123
|
+
for ( var key in stacks ) {
|
|
3124
|
+
|
|
3125
|
+
var stack = stacks[ key ];
|
|
3126
|
+
|
|
3127
|
+
/**
|
|
3128
|
+
* @type {{
|
|
3129
|
+
* name: string,
|
|
3130
|
+
* fps: number,
|
|
3131
|
+
* length: number,
|
|
3132
|
+
* hierarchy: Array.<{
|
|
3133
|
+
* parent: number,
|
|
3134
|
+
* name: string,
|
|
3135
|
+
* keys: Array.<{
|
|
3136
|
+
* time: number,
|
|
3137
|
+
* pos: Array.<number>,
|
|
3138
|
+
* rot: Array.<number>,
|
|
3139
|
+
* scl: Array.<number>
|
|
3140
|
+
* }>
|
|
3141
|
+
* }>
|
|
3142
|
+
* }}
|
|
3143
|
+
*/
|
|
3144
|
+
var animationData = {
|
|
3145
|
+
name: stack.name,
|
|
3146
|
+
fps: 30,
|
|
3147
|
+
length: stack.length,
|
|
3148
|
+
hierarchy: []
|
|
3149
|
+
};
|
|
3150
|
+
|
|
3151
|
+
var bones = group.skeleton.bones;
|
|
3152
|
+
|
|
3153
|
+
for ( var bonesIndex = 0, bonesLength = bones.length; bonesIndex < bonesLength; ++ bonesIndex ) {
|
|
3154
|
+
|
|
3155
|
+
var bone = bones[ bonesIndex ];
|
|
3156
|
+
|
|
3157
|
+
var name = bone.name.replace( /.*:/, '' );
|
|
3158
|
+
var parentIndex = findIndex( bones, function ( parentBone ) {
|
|
3159
|
+
|
|
3160
|
+
return bone.parent === parentBone;
|
|
3161
|
+
|
|
3162
|
+
} );
|
|
3163
|
+
animationData.hierarchy.push( { parent: parentIndex, name: name, keys: [] } );
|
|
3164
|
+
|
|
3165
|
+
}
|
|
3166
|
+
|
|
3167
|
+
for ( var frame = 0; frame <= stack.frames; frame ++ ) {
|
|
3168
|
+
|
|
3169
|
+
for ( var bonesIndex = 0, bonesLength = bones.length; bonesIndex < bonesLength; ++ bonesIndex ) {
|
|
3170
|
+
|
|
3171
|
+
var bone = bones[ bonesIndex ];
|
|
3172
|
+
var boneIndex = bonesIndex;
|
|
3173
|
+
|
|
3174
|
+
var animationNode = stack.layers[ 0 ][ boneIndex ];
|
|
3175
|
+
|
|
3176
|
+
for ( var hierarchyIndex = 0, hierarchyLength = animationData.hierarchy.length; hierarchyIndex < hierarchyLength; ++ hierarchyIndex ) {
|
|
3177
|
+
|
|
3178
|
+
var node = animationData.hierarchy[ hierarchyIndex ];
|
|
3179
|
+
|
|
3180
|
+
if ( node.name === bone.name ) {
|
|
3181
|
+
|
|
3182
|
+
node.keys.push( generateKey( animations, animationNode, bone, frame ) );
|
|
3183
|
+
|
|
3184
|
+
}
|
|
3185
|
+
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
}
|
|
3191
|
+
|
|
3192
|
+
group.animations.push( THREE.AnimationClip.parseAnimation( animationData, bones ) );
|
|
3193
|
+
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
}
|
|
3197
|
+
|
|
3198
|
+
var euler = new THREE.Euler();
|
|
3199
|
+
var quaternion = new THREE.Quaternion();
|
|
3200
|
+
|
|
3201
|
+
/**
|
|
3202
|
+
* @param {THREE.Bone} bone
|
|
3203
|
+
*/
|
|
3204
|
+
function generateKey( animations, animationNode, bone, frame ) {
|
|
3205
|
+
|
|
3206
|
+
var key = {
|
|
3207
|
+
time: frame / animations.fps,
|
|
3208
|
+
pos: bone.position.toArray(),
|
|
3209
|
+
rot: bone.quaternion.toArray(),
|
|
3210
|
+
scl: bone.scale.toArray()
|
|
3211
|
+
};
|
|
3212
|
+
|
|
3213
|
+
if ( animationNode === undefined ) return key;
|
|
3214
|
+
|
|
3215
|
+
try {
|
|
3216
|
+
|
|
3217
|
+
if ( hasCurve( animationNode, 'T' ) && hasKeyOnFrame( animationNode.T, frame ) ) {
|
|
3218
|
+
|
|
3219
|
+
key.pos = [ animationNode.T.curves.x.values[ frame ], animationNode.T.curves.y.values[ frame ], animationNode.T.curves.z.values[ frame ] ];
|
|
3220
|
+
|
|
3221
|
+
}
|
|
3222
|
+
|
|
3223
|
+
if ( hasCurve( animationNode, 'R' ) && hasKeyOnFrame( animationNode.R, frame ) ) {
|
|
3224
|
+
|
|
3225
|
+
var rotationX = animationNode.R.curves.x.values[ frame ];
|
|
3226
|
+
var rotationY = animationNode.R.curves.y.values[ frame ];
|
|
3227
|
+
var rotationZ = animationNode.R.curves.z.values[ frame ];
|
|
3228
|
+
|
|
3229
|
+
quaternion.setFromEuler( euler.set( rotationX, rotationY, rotationZ, 'ZYX' ) );
|
|
3230
|
+
key.rot = quaternion.toArray();
|
|
3231
|
+
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
if ( hasCurve( animationNode, 'S' ) && hasKeyOnFrame( animationNode.S, frame ) ) {
|
|
3235
|
+
|
|
3236
|
+
key.scl = [ animationNode.S.curves.x.values[ frame ], animationNode.S.curves.y.values[ frame ], animationNode.S.curves.z.values[ frame ] ];
|
|
3237
|
+
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
} catch ( error ) {
|
|
3241
|
+
|
|
3242
|
+
// Curve is not fully plotted.
|
|
3243
|
+
console.log( bone );
|
|
3244
|
+
console.log( error );
|
|
3245
|
+
|
|
3246
|
+
}
|
|
3247
|
+
|
|
3248
|
+
return key;
|
|
3249
|
+
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
var AXES = [ 'x', 'y', 'z' ];
|
|
3253
|
+
|
|
3254
|
+
function hasCurve( animationNode, attribute ) {
|
|
3255
|
+
|
|
3256
|
+
if ( animationNode === undefined ) {
|
|
3257
|
+
|
|
3258
|
+
return false;
|
|
3259
|
+
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3262
|
+
var attributeNode = animationNode[ attribute ];
|
|
3263
|
+
|
|
3264
|
+
if ( ! attributeNode ) {
|
|
3265
|
+
|
|
3266
|
+
return false;
|
|
3267
|
+
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3270
|
+
return AXES.every( function ( key ) {
|
|
3271
|
+
|
|
3272
|
+
return attributeNode.curves[ key ] !== null;
|
|
3273
|
+
|
|
3274
|
+
} );
|
|
3275
|
+
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3278
|
+
function hasKeyOnFrame( attributeNode, frame ) {
|
|
3279
|
+
|
|
3280
|
+
return AXES.every( function ( key ) {
|
|
3281
|
+
|
|
3282
|
+
return isKeyExistOnFrame( attributeNode.curves[ key ], frame );
|
|
3283
|
+
|
|
3284
|
+
} );
|
|
3285
|
+
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
function isKeyExistOnFrame( curve, frame ) {
|
|
3289
|
+
|
|
3290
|
+
return curve.values[ frame ] !== undefined;
|
|
3291
|
+
|
|
3292
|
+
}
|
|
3293
|
+
|
|
3294
|
+
/**
|
|
3295
|
+
* An instance of a Vertex with data for drawing vertices to the screen.
|
|
3296
|
+
* @constructor
|
|
3297
|
+
*/
|
|
3298
|
+
function Vertex() {
|
|
3299
|
+
|
|
3300
|
+
/**
|
|
3301
|
+
* Position of the vertex.
|
|
3302
|
+
* @type {THREE.Vector3}
|
|
3303
|
+
*/
|
|
3304
|
+
this.position = new THREE.Vector3();
|
|
3305
|
+
|
|
3306
|
+
/**
|
|
3307
|
+
* Normal of the vertex
|
|
3308
|
+
* @type {THREE.Vector3}
|
|
3309
|
+
*/
|
|
3310
|
+
this.normal = new THREE.Vector3();
|
|
3311
|
+
|
|
3312
|
+
/**
|
|
3313
|
+
* UV coordinates of the vertex.
|
|
3314
|
+
* @type {THREE.Vector2}
|
|
3315
|
+
*/
|
|
3316
|
+
this.uv = new THREE.Vector2();
|
|
3317
|
+
|
|
3318
|
+
/**
|
|
3319
|
+
* Second UV layer coordinates of the vertex.
|
|
3320
|
+
* @type {THREE.Vector2}
|
|
3321
|
+
*/
|
|
3322
|
+
this.uv2 = new THREE.Vector2();
|
|
3323
|
+
|
|
3324
|
+
/**
|
|
3325
|
+
* Color of the vertex
|
|
3326
|
+
* @type {THREE.Vector3}
|
|
3327
|
+
*/
|
|
3328
|
+
this.color = new THREE.Vector3();
|
|
3329
|
+
|
|
3330
|
+
/**
|
|
3331
|
+
* Indices of the bones vertex is influenced by.
|
|
3332
|
+
* @type {THREE.Vector4}
|
|
3333
|
+
*/
|
|
3334
|
+
this.skinIndices = new THREE.Vector4( 0, 0, 0, 0 );
|
|
3335
|
+
|
|
3336
|
+
/**
|
|
3337
|
+
* Weights that each bone influences the vertex.
|
|
3338
|
+
* @type {THREE.Vector4}
|
|
3339
|
+
*/
|
|
3340
|
+
this.skinWeights = new THREE.Vector4( 0, 0, 0, 0 );
|
|
3341
|
+
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
Object.assign( Vertex.prototype, {
|
|
3345
|
+
|
|
3346
|
+
copy: function ( target ) {
|
|
3347
|
+
|
|
3348
|
+
var returnVar = target || new Vertex();
|
|
3349
|
+
|
|
3350
|
+
returnVar.position.copy( this.position );
|
|
3351
|
+
returnVar.normal.copy( this.normal );
|
|
3352
|
+
returnVar.uv.copy( this.uv );
|
|
3353
|
+
returnVar.uv2.copy( this.uv2 );
|
|
3354
|
+
returnVar.skinIndices.copy( this.skinIndices );
|
|
3355
|
+
returnVar.skinWeights.copy( this.skinWeights );
|
|
3356
|
+
|
|
3357
|
+
return returnVar;
|
|
3358
|
+
|
|
3359
|
+
},
|
|
3360
|
+
|
|
3361
|
+
flattenToBuffers: function ( vertexBuffer, normalBuffer, uvBuffer, uv2Buffer, colorBuffer, skinIndexBuffer, skinWeightBuffer ) {
|
|
3362
|
+
|
|
3363
|
+
this.position.toArray( vertexBuffer, vertexBuffer.length );
|
|
3364
|
+
this.normal.toArray( normalBuffer, normalBuffer.length );
|
|
3365
|
+
this.uv.toArray( uvBuffer, uvBuffer.length );
|
|
3366
|
+
this.uv2.toArray( uv2Buffer, uv2Buffer.length );
|
|
3367
|
+
this.color.toArray( colorBuffer, colorBuffer.length );
|
|
3368
|
+
this.skinIndices.toArray( skinIndexBuffer, skinIndexBuffer.length );
|
|
3369
|
+
this.skinWeights.toArray( skinWeightBuffer, skinWeightBuffer.length );
|
|
3370
|
+
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
} );
|
|
3374
|
+
|
|
3375
|
+
/**
|
|
3376
|
+
* @constructor
|
|
3377
|
+
*/
|
|
3378
|
+
function Triangle() {
|
|
3379
|
+
|
|
3380
|
+
/**
|
|
3381
|
+
* @type {{position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]}
|
|
3382
|
+
*/
|
|
3383
|
+
this.vertices = [];
|
|
3384
|
+
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
Object.assign( Triangle.prototype, {
|
|
3388
|
+
|
|
3389
|
+
copy: function ( target ) {
|
|
3390
|
+
|
|
3391
|
+
var returnVar = target || new Triangle();
|
|
3392
|
+
|
|
3393
|
+
for ( var i = 0; i < this.vertices.length; ++ i ) {
|
|
3394
|
+
|
|
3395
|
+
this.vertices[ i ].copy( returnVar.vertices[ i ] );
|
|
3396
|
+
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3399
|
+
return returnVar;
|
|
3400
|
+
|
|
3401
|
+
},
|
|
3402
|
+
|
|
3403
|
+
flattenToBuffers: function ( vertexBuffer, normalBuffer, uvBuffer, uv2Buffer, colorBuffer, skinIndexBuffer, skinWeightBuffer ) {
|
|
3404
|
+
|
|
3405
|
+
var vertices = this.vertices;
|
|
3406
|
+
|
|
3407
|
+
for ( var i = 0, l = vertices.length; i < l; ++ i ) {
|
|
3408
|
+
|
|
3409
|
+
vertices[ i ].flattenToBuffers( vertexBuffer, normalBuffer, uvBuffer, uv2Buffer, colorBuffer, skinIndexBuffer, skinWeightBuffer );
|
|
3410
|
+
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
} );
|
|
3416
|
+
|
|
3417
|
+
/**
|
|
3418
|
+
* @constructor
|
|
3419
|
+
*/
|
|
3420
|
+
function Face() {
|
|
3421
|
+
|
|
3422
|
+
/**
|
|
3423
|
+
* @type {{vertices: {position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]}[]}
|
|
3424
|
+
*/
|
|
3425
|
+
this.triangles = [];
|
|
3426
|
+
this.materialIndex = 0;
|
|
3427
|
+
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3430
|
+
Object.assign( Face.prototype, {
|
|
3431
|
+
|
|
3432
|
+
copy: function ( target ) {
|
|
3433
|
+
|
|
3434
|
+
var returnVar = target || new Face();
|
|
3435
|
+
|
|
3436
|
+
for ( var i = 0; i < this.triangles.length; ++ i ) {
|
|
3437
|
+
|
|
3438
|
+
this.triangles[ i ].copy( returnVar.triangles[ i ] );
|
|
3439
|
+
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3442
|
+
returnVar.materialIndex = this.materialIndex;
|
|
3443
|
+
|
|
3444
|
+
return returnVar;
|
|
3445
|
+
|
|
3446
|
+
},
|
|
3447
|
+
|
|
3448
|
+
genTrianglesFromVertices: function ( vertexArray ) {
|
|
3449
|
+
|
|
3450
|
+
for ( var i = 2; i < vertexArray.length; ++ i ) {
|
|
3451
|
+
|
|
3452
|
+
var triangle = new Triangle();
|
|
3453
|
+
triangle.vertices[ 0 ] = vertexArray[ 0 ];
|
|
3454
|
+
triangle.vertices[ 1 ] = vertexArray[ i - 1 ];
|
|
3455
|
+
triangle.vertices[ 2 ] = vertexArray[ i ];
|
|
3456
|
+
this.triangles.push( triangle );
|
|
3457
|
+
|
|
3458
|
+
}
|
|
3459
|
+
|
|
3460
|
+
},
|
|
3461
|
+
|
|
3462
|
+
flattenToBuffers: function ( vertexBuffer, normalBuffer, uvBuffer, uv2Buffer, colorBuffer, skinIndexBuffer, skinWeightBuffer, materialIndexBuffer ) {
|
|
3463
|
+
|
|
3464
|
+
var triangles = this.triangles;
|
|
3465
|
+
var materialIndex = this.materialIndex;
|
|
3466
|
+
|
|
3467
|
+
for ( var i = 0, l = triangles.length; i < l; ++ i ) {
|
|
3468
|
+
|
|
3469
|
+
triangles[ i ].flattenToBuffers( vertexBuffer, normalBuffer, uvBuffer, uv2Buffer, colorBuffer, skinIndexBuffer, skinWeightBuffer );
|
|
3470
|
+
append( materialIndexBuffer, [ materialIndex, materialIndex, materialIndex ] );
|
|
3471
|
+
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
} );
|
|
3477
|
+
|
|
3478
|
+
/**
|
|
3479
|
+
* @constructor
|
|
3480
|
+
*/
|
|
3481
|
+
function Geometry() {
|
|
3482
|
+
|
|
3483
|
+
/**
|
|
3484
|
+
* @type {{triangles: {vertices: {position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]}[], materialIndex: number}[]}
|
|
3485
|
+
*/
|
|
3486
|
+
this.faces = [];
|
|
3487
|
+
|
|
3488
|
+
/**
|
|
3489
|
+
* @type {{}|THREE.Skeleton}
|
|
3490
|
+
*/
|
|
3491
|
+
this.skeleton = null;
|
|
3492
|
+
|
|
3493
|
+
}
|
|
3494
|
+
|
|
3495
|
+
Object.assign( Geometry.prototype, {
|
|
3496
|
+
|
|
3497
|
+
/**
|
|
3498
|
+
* @returns {{vertexBuffer: number[], normalBuffer: number[], uvBuffer: number[], skinIndexBuffer: number[], skinWeightBuffer: number[], materialIndexBuffer: number[]}}
|
|
3499
|
+
*/
|
|
3500
|
+
flattenToBuffers: function () {
|
|
3501
|
+
|
|
3502
|
+
var vertexBuffer = [];
|
|
3503
|
+
var normalBuffer = [];
|
|
3504
|
+
var uvBuffer = [];
|
|
3505
|
+
var uv2Buffer = [];
|
|
3506
|
+
var colorBuffer = [];
|
|
3507
|
+
var skinIndexBuffer = [];
|
|
3508
|
+
var skinWeightBuffer = [];
|
|
3509
|
+
|
|
3510
|
+
var materialIndexBuffer = [];
|
|
3511
|
+
|
|
3512
|
+
var faces = this.faces;
|
|
3513
|
+
|
|
3514
|
+
for ( var i = 0, l = faces.length; i < l; ++ i ) {
|
|
3515
|
+
|
|
3516
|
+
faces[ i ].flattenToBuffers( vertexBuffer, normalBuffer, uvBuffer, uv2Buffer, colorBuffer, skinIndexBuffer, skinWeightBuffer, materialIndexBuffer );
|
|
3517
|
+
|
|
3518
|
+
}
|
|
3519
|
+
|
|
3520
|
+
return {
|
|
3521
|
+
vertexBuffer: vertexBuffer,
|
|
3522
|
+
normalBuffer: normalBuffer,
|
|
3523
|
+
uvBuffer: uvBuffer,
|
|
3524
|
+
uv2Buffer: uv2Buffer,
|
|
3525
|
+
colorBuffer: colorBuffer,
|
|
3526
|
+
skinIndexBuffer: skinIndexBuffer,
|
|
3527
|
+
skinWeightBuffer: skinWeightBuffer,
|
|
3528
|
+
materialIndexBuffer: materialIndexBuffer
|
|
3529
|
+
};
|
|
3530
|
+
|
|
3531
|
+
}
|
|
3532
|
+
|
|
3533
|
+
} );
|
|
3534
|
+
|
|
3535
|
+
function TextParser() {}
|
|
3536
|
+
|
|
3537
|
+
Object.assign( TextParser.prototype, {
|
|
3538
|
+
|
|
3539
|
+
getPrevNode: function () {
|
|
3540
|
+
|
|
3541
|
+
return this.nodeStack[ this.currentIndent - 2 ];
|
|
3542
|
+
|
|
3543
|
+
},
|
|
3544
|
+
|
|
3545
|
+
getCurrentNode: function () {
|
|
3546
|
+
|
|
3547
|
+
return this.nodeStack[ this.currentIndent - 1 ];
|
|
3548
|
+
|
|
3549
|
+
},
|
|
3550
|
+
|
|
3551
|
+
getCurrentProp: function () {
|
|
3552
|
+
|
|
3553
|
+
return this.currentProp;
|
|
3554
|
+
|
|
3555
|
+
},
|
|
3556
|
+
|
|
3557
|
+
pushStack: function ( node ) {
|
|
3558
|
+
|
|
3559
|
+
this.nodeStack.push( node );
|
|
3560
|
+
this.currentIndent += 1;
|
|
3561
|
+
|
|
3562
|
+
},
|
|
3563
|
+
|
|
3564
|
+
popStack: function () {
|
|
3565
|
+
|
|
3566
|
+
this.nodeStack.pop();
|
|
3567
|
+
this.currentIndent -= 1;
|
|
3568
|
+
|
|
3569
|
+
},
|
|
3570
|
+
|
|
3571
|
+
setCurrentProp: function ( val, name ) {
|
|
3572
|
+
|
|
3573
|
+
this.currentProp = val;
|
|
3574
|
+
this.currentPropName = name;
|
|
3575
|
+
|
|
3576
|
+
},
|
|
3577
|
+
|
|
3578
|
+
// ----------parse ---------------------------------------------------
|
|
3579
|
+
parse: function ( text ) {
|
|
3580
|
+
|
|
3581
|
+
this.currentIndent = 0;
|
|
3582
|
+
this.allNodes = new FBXTree();
|
|
3583
|
+
this.nodeStack = [];
|
|
3584
|
+
this.currentProp = [];
|
|
3585
|
+
this.currentPropName = '';
|
|
3586
|
+
|
|
3587
|
+
var split = text.split( "\n" );
|
|
3588
|
+
|
|
3589
|
+
for ( var line in split ) {
|
|
3590
|
+
|
|
3591
|
+
var l = split[ line ];
|
|
3592
|
+
|
|
3593
|
+
// short cut
|
|
3594
|
+
if ( l.match( /^[\s\t]*;/ ) ) {
|
|
3595
|
+
|
|
3596
|
+
continue;
|
|
3597
|
+
|
|
3598
|
+
} // skip comment line
|
|
3599
|
+
if ( l.match( /^[\s\t]*$/ ) ) {
|
|
3600
|
+
|
|
3601
|
+
continue;
|
|
3602
|
+
|
|
3603
|
+
} // skip empty line
|
|
3604
|
+
|
|
3605
|
+
// beginning of node
|
|
3606
|
+
var beginningOfNodeExp = new RegExp( "^\\t{" + this.currentIndent + "}(\\w+):(.*){", '' );
|
|
3607
|
+
var match = l.match( beginningOfNodeExp );
|
|
3608
|
+
if ( match ) {
|
|
3609
|
+
|
|
3610
|
+
var nodeName = match[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, "" );
|
|
3611
|
+
var nodeAttrs = match[ 2 ].split( ',' );
|
|
3612
|
+
|
|
3613
|
+
for ( var i = 0, l = nodeAttrs.length; i < l; i ++ ) {
|
|
3614
|
+
nodeAttrs[ i ] = nodeAttrs[ i ].trim().replace( /^"/, '' ).replace( /"$/, '' );
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
this.parseNodeBegin( l, nodeName, nodeAttrs || null );
|
|
3618
|
+
continue;
|
|
3619
|
+
|
|
3620
|
+
}
|
|
3621
|
+
|
|
3622
|
+
// node's property
|
|
3623
|
+
var propExp = new RegExp( "^\\t{" + ( this.currentIndent ) + "}(\\w+):[\\s\\t\\r\\n](.*)" );
|
|
3624
|
+
var match = l.match( propExp );
|
|
3625
|
+
if ( match ) {
|
|
3626
|
+
|
|
3627
|
+
var propName = match[ 1 ].replace( /^"/, '' ).replace( /"$/, "" ).trim();
|
|
3628
|
+
var propValue = match[ 2 ].replace( /^"/, '' ).replace( /"$/, "" ).trim();
|
|
3629
|
+
|
|
3630
|
+
this.parseNodeProperty( l, propName, propValue );
|
|
3631
|
+
continue;
|
|
3632
|
+
|
|
3633
|
+
}
|
|
3634
|
+
|
|
3635
|
+
// end of node
|
|
3636
|
+
var endOfNodeExp = new RegExp( "^\\t{" + ( this.currentIndent - 1 ) + "}}" );
|
|
3637
|
+
if ( l.match( endOfNodeExp ) ) {
|
|
3638
|
+
|
|
3639
|
+
this.nodeEnd();
|
|
3640
|
+
continue;
|
|
3641
|
+
|
|
3642
|
+
}
|
|
3643
|
+
|
|
3644
|
+
// for special case,
|
|
3645
|
+
//
|
|
3646
|
+
// Vertices: *8670 {
|
|
3647
|
+
// a: 0.0356229953467846,13.9599733352661,-0.399196773.....(snip)
|
|
3648
|
+
// -0.0612030513584614,13.960485458374,-0.409748703241348,-0.10.....
|
|
3649
|
+
// 0.12490539252758,13.7450733184814,-0.454119384288788,0.09272.....
|
|
3650
|
+
// 0.0836158767342567,13.5432004928589,-0.435397416353226,0.028.....
|
|
3651
|
+
//
|
|
3652
|
+
// these case the lines must contiue with previous line
|
|
3653
|
+
if ( l.match( /^[^\s\t}]/ ) ) {
|
|
3654
|
+
|
|
3655
|
+
this.parseNodePropertyContinued( l );
|
|
3656
|
+
|
|
3657
|
+
}
|
|
3658
|
+
|
|
3659
|
+
}
|
|
3660
|
+
|
|
3661
|
+
return this.allNodes;
|
|
3662
|
+
|
|
3663
|
+
},
|
|
3664
|
+
|
|
3665
|
+
parseNodeBegin: function ( line, nodeName, nodeAttrs ) {
|
|
3666
|
+
|
|
3667
|
+
// var nodeName = match[1];
|
|
3668
|
+
var node = { 'name': nodeName, properties: {}, 'subNodes': {} };
|
|
3669
|
+
var attrs = this.parseNodeAttr( nodeAttrs );
|
|
3670
|
+
var currentNode = this.getCurrentNode();
|
|
3671
|
+
|
|
3672
|
+
// a top node
|
|
3673
|
+
if ( this.currentIndent === 0 ) {
|
|
3674
|
+
|
|
3675
|
+
this.allNodes.add( nodeName, node );
|
|
3676
|
+
|
|
3677
|
+
} else {
|
|
3678
|
+
|
|
3679
|
+
// a subnode
|
|
3680
|
+
|
|
3681
|
+
// already exists subnode, then append it
|
|
3682
|
+
if ( nodeName in currentNode.subNodes ) {
|
|
3683
|
+
|
|
3684
|
+
var tmp = currentNode.subNodes[ nodeName ];
|
|
3685
|
+
|
|
3686
|
+
// console.log( "duped entry found\nkey: " + nodeName + "\nvalue: " + propValue );
|
|
3687
|
+
if ( this.isFlattenNode( currentNode.subNodes[ nodeName ] ) ) {
|
|
3688
|
+
|
|
3689
|
+
|
|
3690
|
+
if ( attrs.id === '' ) {
|
|
3691
|
+
|
|
3692
|
+
currentNode.subNodes[ nodeName ] = [];
|
|
3693
|
+
currentNode.subNodes[ nodeName ].push( tmp );
|
|
3694
|
+
|
|
3695
|
+
} else {
|
|
3696
|
+
|
|
3697
|
+
currentNode.subNodes[ nodeName ] = {};
|
|
3698
|
+
currentNode.subNodes[ nodeName ][ tmp.id ] = tmp;
|
|
3699
|
+
|
|
3700
|
+
}
|
|
3701
|
+
|
|
3702
|
+
}
|
|
3703
|
+
|
|
3704
|
+
if ( attrs.id === '' ) {
|
|
3705
|
+
|
|
3706
|
+
currentNode.subNodes[ nodeName ].push( node );
|
|
3707
|
+
|
|
3708
|
+
} else {
|
|
3709
|
+
|
|
3710
|
+
currentNode.subNodes[ nodeName ][ attrs.id ] = node;
|
|
3711
|
+
|
|
3712
|
+
}
|
|
3713
|
+
|
|
3714
|
+
} else if ( typeof attrs.id === 'number' || attrs.id.match( /^\d+$/ ) ) {
|
|
3715
|
+
|
|
3716
|
+
currentNode.subNodes[ nodeName ] = {};
|
|
3717
|
+
currentNode.subNodes[ nodeName ][ attrs.id ] = node;
|
|
3718
|
+
|
|
3719
|
+
} else {
|
|
3720
|
+
|
|
3721
|
+
currentNode.subNodes[ nodeName ] = node;
|
|
3722
|
+
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
}
|
|
3726
|
+
|
|
3727
|
+
// for this ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
|
|
3728
|
+
// NodeAttribute: 1001463072, "NodeAttribute::", "LimbNode" {
|
|
3729
|
+
if ( nodeAttrs ) {
|
|
3730
|
+
|
|
3731
|
+
node.id = attrs.id;
|
|
3732
|
+
node.attrName = attrs.name;
|
|
3733
|
+
node.attrType = attrs.type;
|
|
3734
|
+
|
|
3735
|
+
}
|
|
3736
|
+
|
|
3737
|
+
this.pushStack( node );
|
|
3738
|
+
|
|
3739
|
+
},
|
|
3740
|
+
|
|
3741
|
+
parseNodeAttr: function ( attrs ) {
|
|
3742
|
+
|
|
3743
|
+
var id = attrs[ 0 ];
|
|
3744
|
+
|
|
3745
|
+
if ( attrs[ 0 ] !== "" ) {
|
|
3746
|
+
|
|
3747
|
+
id = parseInt( attrs[ 0 ] );
|
|
3748
|
+
|
|
3749
|
+
if ( isNaN( id ) ) {
|
|
3750
|
+
|
|
3751
|
+
// PolygonVertexIndex: *16380 {
|
|
3752
|
+
id = attrs[ 0 ];
|
|
3753
|
+
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3756
|
+
}
|
|
3757
|
+
|
|
3758
|
+
var name = '', type = '';
|
|
3759
|
+
|
|
3760
|
+
if ( attrs.length > 1 ) {
|
|
3761
|
+
|
|
3762
|
+
name = attrs[ 1 ].replace( /^(\w+)::/, '' );
|
|
3763
|
+
type = attrs[ 2 ];
|
|
3764
|
+
|
|
3765
|
+
}
|
|
3766
|
+
|
|
3767
|
+
return { id: id, name: name, type: type };
|
|
3768
|
+
|
|
3769
|
+
},
|
|
3770
|
+
|
|
3771
|
+
parseNodeProperty: function ( line, propName, propValue ) {
|
|
3772
|
+
|
|
3773
|
+
var currentNode = this.getCurrentNode();
|
|
3774
|
+
var parentName = currentNode.name;
|
|
3775
|
+
|
|
3776
|
+
// special case parent node's is like "Properties70"
|
|
3777
|
+
// these chilren nodes must treat with careful
|
|
3778
|
+
if ( parentName !== undefined ) {
|
|
3779
|
+
|
|
3780
|
+
var propMatch = parentName.match( /Properties(\d)+/ );
|
|
3781
|
+
if ( propMatch ) {
|
|
3782
|
+
|
|
3783
|
+
this.parseNodeSpecialProperty( line, propName, propValue );
|
|
3784
|
+
return;
|
|
3785
|
+
|
|
3786
|
+
}
|
|
3787
|
+
|
|
3788
|
+
}
|
|
3789
|
+
|
|
3790
|
+
// special case Connections
|
|
3791
|
+
if ( propName == 'C' ) {
|
|
3792
|
+
|
|
3793
|
+
var connProps = propValue.split( ',' ).slice( 1 );
|
|
3794
|
+
var from = parseInt( connProps[ 0 ] );
|
|
3795
|
+
var to = parseInt( connProps[ 1 ] );
|
|
3796
|
+
|
|
3797
|
+
var rest = propValue.split( ',' ).slice( 3 );
|
|
3798
|
+
|
|
3799
|
+
propName = 'connections';
|
|
3800
|
+
propValue = [ from, to ];
|
|
3801
|
+
append( propValue, rest );
|
|
3802
|
+
|
|
3803
|
+
if ( currentNode.properties[ propName ] === undefined ) {
|
|
3804
|
+
|
|
3805
|
+
currentNode.properties[ propName ] = [];
|
|
3806
|
+
|
|
3807
|
+
}
|
|
3808
|
+
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3811
|
+
// special case Connections
|
|
3812
|
+
if ( propName == 'Node' ) {
|
|
3813
|
+
|
|
3814
|
+
var id = parseInt( propValue );
|
|
3815
|
+
currentNode.properties.id = id;
|
|
3816
|
+
currentNode.id = id;
|
|
3817
|
+
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
// already exists in properties, then append this
|
|
3821
|
+
if ( propName in currentNode.properties ) {
|
|
3822
|
+
|
|
3823
|
+
// console.log( "duped entry found\nkey: " + propName + "\nvalue: " + propValue );
|
|
3824
|
+
if ( Array.isArray( currentNode.properties[ propName ] ) ) {
|
|
3825
|
+
|
|
3826
|
+
currentNode.properties[ propName ].push( propValue );
|
|
3827
|
+
|
|
3828
|
+
} else {
|
|
3829
|
+
|
|
3830
|
+
currentNode.properties[ propName ] += propValue;
|
|
3831
|
+
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
} else {
|
|
3835
|
+
|
|
3836
|
+
// console.log( propName + ": " + propValue );
|
|
3837
|
+
if ( Array.isArray( currentNode.properties[ propName ] ) ) {
|
|
3838
|
+
|
|
3839
|
+
currentNode.properties[ propName ].push( propValue );
|
|
3840
|
+
|
|
3841
|
+
} else {
|
|
3842
|
+
|
|
3843
|
+
currentNode.properties[ propName ] = propValue;
|
|
3844
|
+
|
|
3845
|
+
}
|
|
3846
|
+
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3849
|
+
this.setCurrentProp( currentNode.properties, propName );
|
|
3850
|
+
|
|
3851
|
+
},
|
|
3852
|
+
|
|
3853
|
+
// TODO:
|
|
3854
|
+
parseNodePropertyContinued: function ( line ) {
|
|
3855
|
+
|
|
3856
|
+
this.currentProp[ this.currentPropName ] += line;
|
|
3857
|
+
|
|
3858
|
+
},
|
|
3859
|
+
|
|
3860
|
+
parseNodeSpecialProperty: function ( line, propName, propValue ) {
|
|
3861
|
+
|
|
3862
|
+
// split this
|
|
3863
|
+
// P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1
|
|
3864
|
+
// into array like below
|
|
3865
|
+
// ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ]
|
|
3866
|
+
var props = propValue.split( '",' );
|
|
3867
|
+
|
|
3868
|
+
for ( var i = 0, l = props.length; i < l; i ++ ) {
|
|
3869
|
+
props[ i ] = props[ i ].trim().replace( /^\"/, '' ).replace( /\s/, '_' );
|
|
3870
|
+
}
|
|
3871
|
+
|
|
3872
|
+
var innerPropName = props[ 0 ];
|
|
3873
|
+
var innerPropType1 = props[ 1 ];
|
|
3874
|
+
var innerPropType2 = props[ 2 ];
|
|
3875
|
+
var innerPropFlag = props[ 3 ];
|
|
3876
|
+
var innerPropValue = props[ 4 ];
|
|
3877
|
+
|
|
3878
|
+
/*
|
|
3879
|
+
if ( innerPropValue === undefined ) {
|
|
3880
|
+
innerPropValue = props[3];
|
|
3881
|
+
}
|
|
3882
|
+
*/
|
|
3883
|
+
|
|
3884
|
+
// cast value in its type
|
|
3885
|
+
switch ( innerPropType1 ) {
|
|
3886
|
+
|
|
3887
|
+
case "int":
|
|
3888
|
+
innerPropValue = parseInt( innerPropValue );
|
|
3889
|
+
break;
|
|
3890
|
+
|
|
3891
|
+
case "double":
|
|
3892
|
+
innerPropValue = parseFloat( innerPropValue );
|
|
3893
|
+
break;
|
|
3894
|
+
|
|
3895
|
+
case "ColorRGB":
|
|
3896
|
+
case "Vector3D":
|
|
3897
|
+
innerPropValue = parseFloatArray( innerPropValue );
|
|
3898
|
+
break;
|
|
3899
|
+
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
// CAUTION: these props must append to parent's parent
|
|
3903
|
+
this.getPrevNode().properties[ innerPropName ] = {
|
|
3904
|
+
|
|
3905
|
+
'type': innerPropType1,
|
|
3906
|
+
'type2': innerPropType2,
|
|
3907
|
+
'flag': innerPropFlag,
|
|
3908
|
+
'value': innerPropValue
|
|
3909
|
+
|
|
3910
|
+
};
|
|
3911
|
+
|
|
3912
|
+
this.setCurrentProp( this.getPrevNode().properties, innerPropName );
|
|
3913
|
+
|
|
3914
|
+
},
|
|
3915
|
+
|
|
3916
|
+
nodeEnd: function () {
|
|
3917
|
+
|
|
3918
|
+
this.popStack();
|
|
3919
|
+
|
|
3920
|
+
},
|
|
3921
|
+
|
|
3922
|
+
/* ---------------------------------------------------------------- */
|
|
3923
|
+
/* util */
|
|
3924
|
+
isFlattenNode: function ( node ) {
|
|
3925
|
+
|
|
3926
|
+
return ( 'subNodes' in node && 'properties' in node ) ? true : false;
|
|
3927
|
+
|
|
3928
|
+
}
|
|
3929
|
+
|
|
3930
|
+
} );
|
|
3931
|
+
|
|
3932
|
+
// Binary format specification:
|
|
3933
|
+
// https://code.blender.org/2013/08/fbx-binary-file-format-specification/
|
|
3934
|
+
// https://wiki.rogiken.org/specifications/file-format/fbx/ (more detail but Japanese)
|
|
3935
|
+
function BinaryParser() {}
|
|
3936
|
+
|
|
3937
|
+
Object.assign( BinaryParser.prototype, {
|
|
3938
|
+
|
|
3939
|
+
/**
|
|
3940
|
+
* Parses binary data and builds FBXTree as much compatible as possible with the one built by TextParser.
|
|
3941
|
+
* @param {ArrayBuffer} buffer
|
|
3942
|
+
* @returns {THREE.FBXTree}
|
|
3943
|
+
*/
|
|
3944
|
+
parse: function ( buffer ) {
|
|
3945
|
+
|
|
3946
|
+
var reader = new BinaryReader( buffer );
|
|
3947
|
+
reader.skip( 23 ); // skip magic 23 bytes
|
|
3948
|
+
|
|
3949
|
+
var version = reader.getUint32();
|
|
3950
|
+
|
|
3951
|
+
console.log( 'FBX binary version: ' + version );
|
|
3952
|
+
|
|
3953
|
+
var allNodes = new FBXTree();
|
|
3954
|
+
|
|
3955
|
+
while ( ! this.endOfContent( reader ) ) {
|
|
3956
|
+
|
|
3957
|
+
var node = this.parseNode( reader, version );
|
|
3958
|
+
if ( node !== null ) allNodes.add( node.name, node );
|
|
3959
|
+
|
|
3960
|
+
}
|
|
3961
|
+
|
|
3962
|
+
return allNodes;
|
|
3963
|
+
|
|
3964
|
+
},
|
|
3965
|
+
|
|
3966
|
+
/**
|
|
3967
|
+
* Checks if reader has reached the end of content.
|
|
3968
|
+
* @param {BinaryReader} reader
|
|
3969
|
+
* @returns {boolean}
|
|
3970
|
+
*/
|
|
3971
|
+
endOfContent: function( reader ) {
|
|
3972
|
+
|
|
3973
|
+
// footer size: 160bytes + 16-byte alignment padding
|
|
3974
|
+
// - 16bytes: magic
|
|
3975
|
+
// - padding til 16-byte alignment (at least 1byte?)
|
|
3976
|
+
// (seems like some exporters embed fixed 15bytes?)
|
|
3977
|
+
// - 4bytes: magic
|
|
3978
|
+
// - 4bytes: version
|
|
3979
|
+
// - 120bytes: zero
|
|
3980
|
+
// - 16bytes: magic
|
|
3981
|
+
if ( reader.size() % 16 === 0 ) {
|
|
3982
|
+
|
|
3983
|
+
return ( ( reader.getOffset() + 160 + 16 ) & ~0xf ) >= reader.size();
|
|
3984
|
+
|
|
3985
|
+
} else {
|
|
3986
|
+
|
|
3987
|
+
return reader.getOffset() + 160 + 15 >= reader.size();
|
|
3988
|
+
|
|
3989
|
+
}
|
|
3990
|
+
|
|
3991
|
+
},
|
|
3992
|
+
|
|
3993
|
+
/**
|
|
3994
|
+
* Parses Node as much compatible as possible with the one parsed by TextParser
|
|
3995
|
+
* TODO: could be optimized more?
|
|
3996
|
+
* @param {BinaryReader} reader
|
|
3997
|
+
* @param {number} version
|
|
3998
|
+
* @returns {Object} - Returns an Object as node, or null if NULL-record.
|
|
3999
|
+
*/
|
|
4000
|
+
parseNode: function ( reader, version ) {
|
|
4001
|
+
|
|
4002
|
+
// The first three data sizes depends on version.
|
|
4003
|
+
var endOffset = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32();
|
|
4004
|
+
var numProperties = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32();
|
|
4005
|
+
var propertyListLen = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32();
|
|
4006
|
+
var nameLen = reader.getUint8();
|
|
4007
|
+
var name = reader.getString( nameLen );
|
|
4008
|
+
|
|
4009
|
+
// Regards this node as NULL-record if endOffset is zero
|
|
4010
|
+
if ( endOffset === 0 ) return null;
|
|
4011
|
+
|
|
4012
|
+
var propertyList = [];
|
|
4013
|
+
|
|
4014
|
+
for ( var i = 0; i < numProperties; i ++ ) {
|
|
4015
|
+
|
|
4016
|
+
propertyList.push( this.parseProperty( reader ) );
|
|
4017
|
+
|
|
4018
|
+
}
|
|
4019
|
+
|
|
4020
|
+
// Regards the first three elements in propertyList as id, attrName, and attrType
|
|
4021
|
+
var id = propertyList.length > 0 ? propertyList[ 0 ] : '';
|
|
4022
|
+
var attrName = propertyList.length > 1 ? propertyList[ 1 ] : '';
|
|
4023
|
+
var attrType = propertyList.length > 2 ? propertyList[ 2 ] : '';
|
|
4024
|
+
|
|
4025
|
+
var subNodes = {};
|
|
4026
|
+
var properties = {};
|
|
4027
|
+
|
|
4028
|
+
var isSingleProperty = false;
|
|
4029
|
+
|
|
4030
|
+
// if this node represents just a single property
|
|
4031
|
+
// like (name, 0) set or (name2, [0, 1, 2]) set of {name: 0, name2: [0, 1, 2]}
|
|
4032
|
+
if ( numProperties === 1 && reader.getOffset() === endOffset ) {
|
|
4033
|
+
|
|
4034
|
+
isSingleProperty = true;
|
|
4035
|
+
|
|
4036
|
+
}
|
|
4037
|
+
|
|
4038
|
+
while ( endOffset > reader.getOffset() ) {
|
|
4039
|
+
|
|
4040
|
+
var node = this.parseNode( reader, version );
|
|
4041
|
+
|
|
4042
|
+
if ( node === null ) continue;
|
|
4043
|
+
|
|
4044
|
+
// special case: child node is single property
|
|
4045
|
+
if ( node.singleProperty === true ) {
|
|
4046
|
+
|
|
4047
|
+
var value = node.propertyList[ 0 ];
|
|
4048
|
+
|
|
4049
|
+
if ( Array.isArray( value ) ) {
|
|
4050
|
+
|
|
4051
|
+
// node represents
|
|
4052
|
+
// Vertices: *3 {
|
|
4053
|
+
// a: 0.01, 0.02, 0.03
|
|
4054
|
+
// }
|
|
4055
|
+
// of text format here.
|
|
4056
|
+
|
|
4057
|
+
node.properties[ node.name ] = node.propertyList[ 0 ];
|
|
4058
|
+
subNodes[ node.name ] = node;
|
|
4059
|
+
|
|
4060
|
+
// Later phase expects single property array is in node.properties.a as String.
|
|
4061
|
+
// TODO: optimize
|
|
4062
|
+
node.properties.a = value.toString();
|
|
4063
|
+
|
|
4064
|
+
} else {
|
|
4065
|
+
|
|
4066
|
+
// node represents
|
|
4067
|
+
// Version: 100
|
|
4068
|
+
// of text format here.
|
|
4069
|
+
|
|
4070
|
+
properties[ node.name ] = value;
|
|
4071
|
+
|
|
4072
|
+
}
|
|
4073
|
+
|
|
4074
|
+
continue;
|
|
4075
|
+
|
|
4076
|
+
}
|
|
4077
|
+
|
|
4078
|
+
// special case: connections
|
|
4079
|
+
if ( name === 'Connections' && node.name === 'C' ) {
|
|
4080
|
+
|
|
4081
|
+
var array = [];
|
|
4082
|
+
|
|
4083
|
+
// node.propertyList would be like
|
|
4084
|
+
// ["OO", 111264976, 144038752, "d|x"] (?, from, to, additional values)
|
|
4085
|
+
for ( var i = 1, il = node.propertyList.length; i < il; i ++ ) {
|
|
4086
|
+
|
|
4087
|
+
array[ i - 1 ] = node.propertyList[ i ];
|
|
4088
|
+
|
|
4089
|
+
}
|
|
4090
|
+
|
|
4091
|
+
if ( properties.connections === undefined ) {
|
|
4092
|
+
|
|
4093
|
+
properties.connections = [];
|
|
4094
|
+
|
|
4095
|
+
}
|
|
4096
|
+
|
|
4097
|
+
properties.connections.push( array );
|
|
4098
|
+
|
|
4099
|
+
continue;
|
|
4100
|
+
|
|
4101
|
+
}
|
|
4102
|
+
|
|
4103
|
+
// special case: child node is Properties\d+
|
|
4104
|
+
if ( node.name.match( /^Properties\d+$/ ) ) {
|
|
4105
|
+
|
|
4106
|
+
// move child node's properties to this node.
|
|
4107
|
+
|
|
4108
|
+
var keys = Object.keys( node.properties );
|
|
4109
|
+
|
|
4110
|
+
for ( var i = 0, il = keys.length; i < il; i ++ ) {
|
|
4111
|
+
|
|
4112
|
+
var key = keys[ i ];
|
|
4113
|
+
properties[ key ] = node.properties[ key ];
|
|
4114
|
+
|
|
4115
|
+
}
|
|
4116
|
+
|
|
4117
|
+
continue;
|
|
4118
|
+
|
|
4119
|
+
}
|
|
4120
|
+
|
|
4121
|
+
// special case: properties
|
|
4122
|
+
if ( name.match( /^Properties\d+$/ ) && node.name === 'P' ) {
|
|
4123
|
+
|
|
4124
|
+
var innerPropName = node.propertyList[ 0 ];
|
|
4125
|
+
var innerPropType1 = node.propertyList[ 1 ];
|
|
4126
|
+
var innerPropType2 = node.propertyList[ 2 ];
|
|
4127
|
+
var innerPropFlag = node.propertyList[ 3 ];
|
|
4128
|
+
var innerPropValue;
|
|
4129
|
+
|
|
4130
|
+
if ( innerPropName.indexOf( 'Lcl ' ) === 0 ) innerPropName = innerPropName.replace( 'Lcl ', 'Lcl_' );
|
|
4131
|
+
if ( innerPropType1.indexOf( 'Lcl ' ) === 0 ) innerPropType1 = innerPropType1.replace( 'Lcl ', 'Lcl_' );
|
|
4132
|
+
|
|
4133
|
+
if ( innerPropType1 === 'ColorRGB' || innerPropType1 === 'Vector' ||
|
|
4134
|
+
innerPropType1 === 'Vector3D' || innerPropType1.indexOf( 'Lcl_' ) === 0 ) {
|
|
4135
|
+
|
|
4136
|
+
innerPropValue = [
|
|
4137
|
+
node.propertyList[ 4 ],
|
|
4138
|
+
node.propertyList[ 5 ],
|
|
4139
|
+
node.propertyList[ 6 ]
|
|
4140
|
+
];
|
|
4141
|
+
|
|
4142
|
+
} else {
|
|
4143
|
+
|
|
4144
|
+
innerPropValue = node.propertyList[ 4 ];
|
|
4145
|
+
|
|
4146
|
+
}
|
|
4147
|
+
|
|
4148
|
+
if ( innerPropType1.indexOf( 'Lcl_' ) === 0 ) {
|
|
4149
|
+
|
|
4150
|
+
innerPropValue = innerPropValue.toString();
|
|
4151
|
+
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
// this will be copied to parent. see above.
|
|
4155
|
+
properties[ innerPropName ] = {
|
|
4156
|
+
|
|
4157
|
+
'type': innerPropType1,
|
|
4158
|
+
'type2': innerPropType2,
|
|
4159
|
+
'flag': innerPropFlag,
|
|
4160
|
+
'value': innerPropValue
|
|
4161
|
+
|
|
4162
|
+
};
|
|
4163
|
+
|
|
4164
|
+
continue;
|
|
4165
|
+
|
|
4166
|
+
}
|
|
4167
|
+
|
|
4168
|
+
// standard case
|
|
4169
|
+
// follows TextParser's manner.
|
|
4170
|
+
if ( subNodes[ node.name ] === undefined ) {
|
|
4171
|
+
|
|
4172
|
+
if ( typeof node.id === 'number' ) {
|
|
4173
|
+
|
|
4174
|
+
subNodes[ node.name ] = {};
|
|
4175
|
+
subNodes[ node.name ][ node.id ] = node;
|
|
4176
|
+
|
|
4177
|
+
} else {
|
|
4178
|
+
|
|
4179
|
+
subNodes[ node.name ] = node;
|
|
4180
|
+
|
|
4181
|
+
}
|
|
4182
|
+
|
|
4183
|
+
} else {
|
|
4184
|
+
|
|
4185
|
+
if ( node.id === '' ) {
|
|
4186
|
+
|
|
4187
|
+
if ( ! Array.isArray( subNodes[ node.name ] ) ) {
|
|
4188
|
+
|
|
4189
|
+
subNodes[ node.name ] = [ subNodes[ node.name ] ];
|
|
4190
|
+
|
|
4191
|
+
}
|
|
4192
|
+
|
|
4193
|
+
subNodes[ node.name ].push( node );
|
|
4194
|
+
|
|
4195
|
+
} else {
|
|
4196
|
+
|
|
4197
|
+
if ( subNodes[ node.name ][ node.id ] === undefined ) {
|
|
4198
|
+
|
|
4199
|
+
subNodes[ node.name ][ node.id ] = node;
|
|
4200
|
+
|
|
4201
|
+
} else {
|
|
4202
|
+
|
|
4203
|
+
// conflict id. irregular?
|
|
4204
|
+
|
|
4205
|
+
if ( ! Array.isArray( subNodes[ node.name ][ node.id ] ) ) {
|
|
4206
|
+
|
|
4207
|
+
subNodes[ node.name ][ node.id ] = [ subNodes[ node.name ][ node.id ] ];
|
|
4208
|
+
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
subNodes[ node.name ][ node.id ].push( node );
|
|
4212
|
+
|
|
4213
|
+
}
|
|
4214
|
+
|
|
4215
|
+
}
|
|
4216
|
+
|
|
4217
|
+
}
|
|
4218
|
+
|
|
4219
|
+
}
|
|
4220
|
+
|
|
4221
|
+
return {
|
|
4222
|
+
|
|
4223
|
+
singleProperty: isSingleProperty,
|
|
4224
|
+
id: id,
|
|
4225
|
+
attrName: attrName,
|
|
4226
|
+
attrType: attrType,
|
|
4227
|
+
name: name,
|
|
4228
|
+
properties: properties,
|
|
4229
|
+
propertyList: propertyList, // raw property list, would be used by parent
|
|
4230
|
+
subNodes: subNodes
|
|
4231
|
+
|
|
4232
|
+
};
|
|
4233
|
+
|
|
4234
|
+
},
|
|
4235
|
+
|
|
4236
|
+
parseProperty: function ( reader ) {
|
|
4237
|
+
|
|
4238
|
+
var type = reader.getChar();
|
|
4239
|
+
|
|
4240
|
+
switch ( type ) {
|
|
4241
|
+
|
|
4242
|
+
case 'F':
|
|
4243
|
+
return reader.getFloat32();
|
|
4244
|
+
|
|
4245
|
+
case 'D':
|
|
4246
|
+
return reader.getFloat64();
|
|
4247
|
+
|
|
4248
|
+
case 'L':
|
|
4249
|
+
return reader.getInt64();
|
|
4250
|
+
|
|
4251
|
+
case 'I':
|
|
4252
|
+
return reader.getInt32();
|
|
4253
|
+
|
|
4254
|
+
case 'Y':
|
|
4255
|
+
return reader.getInt16();
|
|
4256
|
+
|
|
4257
|
+
case 'C':
|
|
4258
|
+
return reader.getBoolean();
|
|
4259
|
+
|
|
4260
|
+
case 'f':
|
|
4261
|
+
case 'd':
|
|
4262
|
+
case 'l':
|
|
4263
|
+
case 'i':
|
|
4264
|
+
case 'b':
|
|
4265
|
+
|
|
4266
|
+
var arrayLength = reader.getUint32();
|
|
4267
|
+
var encoding = reader.getUint32(); // 0: non-compressed, 1: compressed
|
|
4268
|
+
var compressedLength = reader.getUint32();
|
|
4269
|
+
|
|
4270
|
+
if ( encoding === 0 ) {
|
|
4271
|
+
|
|
4272
|
+
switch ( type ) {
|
|
4273
|
+
|
|
4274
|
+
case 'f':
|
|
4275
|
+
return reader.getFloat32Array( arrayLength );
|
|
4276
|
+
|
|
4277
|
+
case 'd':
|
|
4278
|
+
return reader.getFloat64Array( arrayLength );
|
|
4279
|
+
|
|
4280
|
+
case 'l':
|
|
4281
|
+
return reader.getInt64Array( arrayLength );
|
|
4282
|
+
|
|
4283
|
+
case 'i':
|
|
4284
|
+
return reader.getInt32Array( arrayLength );
|
|
4285
|
+
|
|
4286
|
+
case 'b':
|
|
4287
|
+
return reader.getBooleanArray( arrayLength );
|
|
4288
|
+
|
|
4289
|
+
}
|
|
4290
|
+
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
if ( self.Zlib === undefined ) {
|
|
4294
|
+
|
|
4295
|
+
throw new Error( 'FBXLoader: Import inflate.min.js from https://github.com/imaya/zlib.js' );
|
|
4296
|
+
|
|
4297
|
+
}
|
|
4298
|
+
|
|
4299
|
+
var inflate = new Zlib.Inflate( new Uint8Array( reader.getArrayBuffer( compressedLength ) ) );
|
|
4300
|
+
var reader2 = new BinaryReader( inflate.decompress().buffer );
|
|
4301
|
+
|
|
4302
|
+
switch ( type ) {
|
|
4303
|
+
|
|
4304
|
+
case 'f':
|
|
4305
|
+
return reader2.getFloat32Array( arrayLength );
|
|
4306
|
+
|
|
4307
|
+
case 'd':
|
|
4308
|
+
return reader2.getFloat64Array( arrayLength );
|
|
4309
|
+
|
|
4310
|
+
case 'l':
|
|
4311
|
+
return reader2.getInt64Array( arrayLength );
|
|
4312
|
+
|
|
4313
|
+
case 'i':
|
|
4314
|
+
return reader2.getInt32Array( arrayLength );
|
|
4315
|
+
|
|
4316
|
+
case 'b':
|
|
4317
|
+
return reader2.getBooleanArray( arrayLength );
|
|
4318
|
+
|
|
4319
|
+
}
|
|
4320
|
+
|
|
4321
|
+
case 'S':
|
|
4322
|
+
var length = reader.getUint32();
|
|
4323
|
+
return reader.getString( length );
|
|
4324
|
+
|
|
4325
|
+
case 'R':
|
|
4326
|
+
var length = reader.getUint32();
|
|
4327
|
+
return reader.getArrayBuffer( length );
|
|
4328
|
+
|
|
4329
|
+
default:
|
|
4330
|
+
throw new Error( 'FBXLoader: Unknown property type ' + type );
|
|
4331
|
+
|
|
4332
|
+
}
|
|
4333
|
+
|
|
4334
|
+
}
|
|
4335
|
+
|
|
4336
|
+
} );
|
|
4337
|
+
|
|
4338
|
+
|
|
4339
|
+
function BinaryReader( buffer, littleEndian ) {
|
|
4340
|
+
|
|
4341
|
+
this.dv = new DataView( buffer );
|
|
4342
|
+
this.offset = 0;
|
|
4343
|
+
this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true;
|
|
4344
|
+
|
|
4345
|
+
}
|
|
4346
|
+
|
|
4347
|
+
Object.assign( BinaryReader.prototype, {
|
|
4348
|
+
|
|
4349
|
+
getOffset: function () {
|
|
4350
|
+
|
|
4351
|
+
return this.offset;
|
|
4352
|
+
|
|
4353
|
+
},
|
|
4354
|
+
|
|
4355
|
+
size: function () {
|
|
4356
|
+
|
|
4357
|
+
return this.dv.buffer.byteLength;
|
|
4358
|
+
|
|
4359
|
+
},
|
|
4360
|
+
|
|
4361
|
+
skip: function ( length ) {
|
|
4362
|
+
|
|
4363
|
+
this.offset += length;
|
|
4364
|
+
|
|
4365
|
+
},
|
|
4366
|
+
|
|
4367
|
+
// seems like true/false representation depends on exporter.
|
|
4368
|
+
// true: 1 or 'Y'(=0x59), false: 0 or 'T'(=0x54)
|
|
4369
|
+
// then sees LSB.
|
|
4370
|
+
getBoolean: function () {
|
|
4371
|
+
|
|
4372
|
+
return ( this.getUint8() & 1 ) === 1;
|
|
4373
|
+
|
|
4374
|
+
},
|
|
4375
|
+
|
|
4376
|
+
getBooleanArray: function ( size ) {
|
|
4377
|
+
|
|
4378
|
+
var a = [];
|
|
4379
|
+
|
|
4380
|
+
for ( var i = 0; i < size; i ++ ) {
|
|
4381
|
+
|
|
4382
|
+
a.push( this.getBoolean() );
|
|
4383
|
+
|
|
4384
|
+
}
|
|
4385
|
+
|
|
4386
|
+
return a;
|
|
4387
|
+
|
|
4388
|
+
},
|
|
4389
|
+
|
|
4390
|
+
getInt8: function () {
|
|
4391
|
+
|
|
4392
|
+
var value = this.dv.getInt8( this.offset );
|
|
4393
|
+
this.offset += 1;
|
|
4394
|
+
return value;
|
|
4395
|
+
|
|
4396
|
+
},
|
|
4397
|
+
|
|
4398
|
+
getInt8Array: function ( size ) {
|
|
4399
|
+
|
|
4400
|
+
var a = [];
|
|
4401
|
+
|
|
4402
|
+
for ( var i = 0; i < size; i ++ ) {
|
|
4403
|
+
|
|
4404
|
+
a.push( this.getInt8() );
|
|
4405
|
+
|
|
4406
|
+
}
|
|
4407
|
+
|
|
4408
|
+
return a;
|
|
4409
|
+
|
|
4410
|
+
},
|
|
4411
|
+
|
|
4412
|
+
getUint8: function () {
|
|
4413
|
+
|
|
4414
|
+
var value = this.dv.getUint8( this.offset );
|
|
4415
|
+
this.offset += 1;
|
|
4416
|
+
return value;
|
|
4417
|
+
|
|
4418
|
+
},
|
|
4419
|
+
|
|
4420
|
+
getUint8Array: function ( size ) {
|
|
4421
|
+
|
|
4422
|
+
var a = [];
|
|
4423
|
+
|
|
4424
|
+
for ( var i = 0; i < size; i ++ ) {
|
|
4425
|
+
|
|
4426
|
+
a.push( this.getUint8() );
|
|
4427
|
+
|
|
4428
|
+
}
|
|
4429
|
+
|
|
4430
|
+
return a;
|
|
4431
|
+
|
|
4432
|
+
},
|
|
4433
|
+
|
|
4434
|
+
getInt16: function () {
|
|
4435
|
+
|
|
4436
|
+
var value = this.dv.getInt16( this.offset, this.littleEndian );
|
|
4437
|
+
this.offset += 2;
|
|
4438
|
+
return value;
|
|
4439
|
+
|
|
4440
|
+
},
|
|
4441
|
+
|
|
4442
|
+
getInt16Array: function ( size ) {
|
|
4443
|
+
|
|
4444
|
+
var a = [];
|
|
4445
|
+
|
|
4446
|
+
for ( var i = 0; i < size; i ++ ) {
|
|
4447
|
+
|
|
4448
|
+
a.push( this.getInt16() );
|
|
4449
|
+
|
|
4450
|
+
}
|
|
4451
|
+
|
|
4452
|
+
return a;
|
|
4453
|
+
|
|
4454
|
+
},
|
|
4455
|
+
|
|
4456
|
+
getUint16: function () {
|
|
4457
|
+
|
|
4458
|
+
var value = this.dv.getUint16( this.offset, this.littleEndian );
|
|
4459
|
+
this.offset += 2;
|
|
4460
|
+
return value;
|
|
4461
|
+
|
|
4462
|
+
},
|
|
4463
|
+
|
|
4464
|
+
getUint16Array: function ( size ) {
|
|
4465
|
+
|
|
4466
|
+
var a = [];
|
|
4467
|
+
|
|
4468
|
+
for ( var i = 0; i < size; i ++ ) {
|
|
4469
|
+
|
|
4470
|
+
a.push( this.getUint16() );
|
|
4471
|
+
|
|
4472
|
+
}
|
|
4473
|
+
|
|
4474
|
+
return a;
|
|
4475
|
+
|
|
4476
|
+
},
|
|
4477
|
+
|
|
4478
|
+
getInt32: function () {
|
|
4479
|
+
|
|
4480
|
+
var value = this.dv.getInt32( this.offset, this.littleEndian );
|
|
4481
|
+
this.offset += 4;
|
|
4482
|
+
return value;
|
|
4483
|
+
|
|
4484
|
+
},
|
|
4485
|
+
|
|
4486
|
+
getInt32Array: function ( size ) {
|
|
4487
|
+
|
|
4488
|
+
var a = [];
|
|
4489
|
+
|
|
4490
|
+
for ( var i = 0; i < size; i ++ ) {
|
|
4491
|
+
|
|
4492
|
+
a.push( this.getInt32() );
|
|
4493
|
+
|
|
4494
|
+
}
|
|
4495
|
+
|
|
4496
|
+
return a;
|
|
4497
|
+
|
|
4498
|
+
},
|
|
4499
|
+
|
|
4500
|
+
getUint32: function () {
|
|
4501
|
+
|
|
4502
|
+
var value = this.dv.getUint32( this.offset, this.littleEndian );
|
|
4503
|
+
this.offset += 4;
|
|
4504
|
+
return value;
|
|
4505
|
+
|
|
4506
|
+
},
|
|
4507
|
+
|
|
4508
|
+
getUint32Array: function ( size ) {
|
|
4509
|
+
|
|
4510
|
+
var a = [];
|
|
4511
|
+
|
|
4512
|
+
for ( var i = 0; i < size; i ++ ) {
|
|
4513
|
+
|
|
4514
|
+
a.push( this.getUint32() );
|
|
4515
|
+
|
|
4516
|
+
}
|
|
4517
|
+
|
|
4518
|
+
return a;
|
|
4519
|
+
|
|
4520
|
+
},
|
|
4521
|
+
|
|
4522
|
+
// JavaScript doesn't support 64-bit integer so attempting to calculate by ourselves.
|
|
4523
|
+
// 1 << 32 will return 1 so using multiply operation instead here.
|
|
4524
|
+
// There'd be a possibility that this method returns wrong value if the value
|
|
4525
|
+
// is out of the range between Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER.
|
|
4526
|
+
// TODO: safely handle 64-bit integer
|
|
4527
|
+
getInt64: function () {
|
|
4528
|
+
|
|
4529
|
+
var low, high;
|
|
4530
|
+
|
|
4531
|
+
if ( this.littleEndian ) {
|
|
4532
|
+
|
|
4533
|
+
low = this.getUint32();
|
|
4534
|
+
high = this.getUint32();
|
|
4535
|
+
|
|
4536
|
+
} else {
|
|
4537
|
+
|
|
4538
|
+
high = this.getUint32();
|
|
4539
|
+
low = this.getUint32();
|
|
4540
|
+
|
|
4541
|
+
}
|
|
4542
|
+
|
|
4543
|
+
// calculate negative value
|
|
4544
|
+
if ( high & 0x80000000 ) {
|
|
4545
|
+
|
|
4546
|
+
high = ~high & 0xFFFFFFFF;
|
|
4547
|
+
low = ~low & 0xFFFFFFFF;
|
|
4548
|
+
|
|
4549
|
+
if ( low === 0xFFFFFFFF ) high = ( high + 1 ) & 0xFFFFFFFF;
|
|
4550
|
+
|
|
4551
|
+
low = ( low + 1 ) & 0xFFFFFFFF;
|
|
4552
|
+
|
|
4553
|
+
return - ( high * 0x100000000 + low );
|
|
4554
|
+
|
|
4555
|
+
}
|
|
4556
|
+
|
|
4557
|
+
return high * 0x100000000 + low;
|
|
4558
|
+
|
|
4559
|
+
},
|
|
4560
|
+
|
|
4561
|
+
getInt64Array: function ( size ) {
|
|
4562
|
+
|
|
4563
|
+
var a = [];
|
|
4564
|
+
|
|
4565
|
+
for ( var i = 0; i < size; i ++ ) {
|
|
4566
|
+
|
|
4567
|
+
a.push( this.getInt64() );
|
|
4568
|
+
|
|
4569
|
+
}
|
|
4570
|
+
|
|
4571
|
+
return a;
|
|
4572
|
+
|
|
4573
|
+
},
|
|
4574
|
+
|
|
4575
|
+
// Note: see getInt64() comment
|
|
4576
|
+
getUint64: function () {
|
|
4577
|
+
|
|
4578
|
+
var low, high;
|
|
4579
|
+
|
|
4580
|
+
if ( this.littleEndian ) {
|
|
4581
|
+
|
|
4582
|
+
low = this.getUint32();
|
|
4583
|
+
high = this.getUint32();
|
|
4584
|
+
|
|
4585
|
+
} else {
|
|
4586
|
+
|
|
4587
|
+
high = this.getUint32();
|
|
4588
|
+
low = this.getUint32();
|
|
4589
|
+
|
|
4590
|
+
}
|
|
4591
|
+
|
|
4592
|
+
return high * 0x100000000 + low;
|
|
4593
|
+
|
|
4594
|
+
},
|
|
4595
|
+
|
|
4596
|
+
getUint64Array: function ( size ) {
|
|
4597
|
+
|
|
4598
|
+
var a = [];
|
|
4599
|
+
|
|
4600
|
+
for ( var i = 0; i < size; i ++ ) {
|
|
4601
|
+
|
|
4602
|
+
a.push( this.getUint64() );
|
|
4603
|
+
|
|
4604
|
+
}
|
|
4605
|
+
|
|
4606
|
+
return a;
|
|
4607
|
+
|
|
4608
|
+
},
|
|
4609
|
+
|
|
4610
|
+
getFloat32: function () {
|
|
4611
|
+
|
|
4612
|
+
var value = this.dv.getFloat32( this.offset, this.littleEndian );
|
|
4613
|
+
this.offset += 4;
|
|
4614
|
+
return value;
|
|
4615
|
+
|
|
4616
|
+
},
|
|
4617
|
+
|
|
4618
|
+
getFloat32Array: function ( size ) {
|
|
4619
|
+
|
|
4620
|
+
var a = [];
|
|
4621
|
+
|
|
4622
|
+
for ( var i = 0; i < size; i ++ ) {
|
|
4623
|
+
|
|
4624
|
+
a.push( this.getFloat32() );
|
|
4625
|
+
|
|
4626
|
+
}
|
|
4627
|
+
|
|
4628
|
+
return a;
|
|
4629
|
+
|
|
4630
|
+
},
|
|
4631
|
+
|
|
4632
|
+
getFloat64: function () {
|
|
4633
|
+
|
|
4634
|
+
var value = this.dv.getFloat64( this.offset, this.littleEndian );
|
|
4635
|
+
this.offset += 8;
|
|
4636
|
+
return value;
|
|
4637
|
+
|
|
4638
|
+
},
|
|
4639
|
+
|
|
4640
|
+
getFloat64Array: function ( size ) {
|
|
4641
|
+
|
|
4642
|
+
var a = [];
|
|
4643
|
+
|
|
4644
|
+
for ( var i = 0; i < size; i ++ ) {
|
|
4645
|
+
|
|
4646
|
+
a.push( this.getFloat64() );
|
|
4647
|
+
|
|
4648
|
+
}
|
|
4649
|
+
|
|
4650
|
+
return a;
|
|
4651
|
+
|
|
4652
|
+
},
|
|
4653
|
+
|
|
4654
|
+
getArrayBuffer: function ( size ) {
|
|
4655
|
+
|
|
4656
|
+
var value = this.dv.buffer.slice( this.offset, this.offset + size );
|
|
4657
|
+
this.offset += size;
|
|
4658
|
+
return value;
|
|
4659
|
+
|
|
4660
|
+
},
|
|
4661
|
+
|
|
4662
|
+
getChar: function () {
|
|
4663
|
+
|
|
4664
|
+
return String.fromCharCode( this.getUint8() );
|
|
4665
|
+
|
|
4666
|
+
},
|
|
4667
|
+
|
|
4668
|
+
getString: function ( size ) {
|
|
4669
|
+
|
|
4670
|
+
var s = '';
|
|
4671
|
+
|
|
4672
|
+
while ( size > 0 ) {
|
|
4673
|
+
|
|
4674
|
+
var value = this.getUint8();
|
|
4675
|
+
size--;
|
|
4676
|
+
|
|
4677
|
+
if ( value === 0 ) break;
|
|
4678
|
+
|
|
4679
|
+
s += String.fromCharCode( value );
|
|
4680
|
+
|
|
4681
|
+
}
|
|
4682
|
+
|
|
4683
|
+
this.skip( size );
|
|
4684
|
+
|
|
4685
|
+
return s;
|
|
4686
|
+
|
|
4687
|
+
}
|
|
4688
|
+
|
|
4689
|
+
} );
|
|
4690
|
+
|
|
4691
|
+
|
|
4692
|
+
function FBXTree() {}
|
|
4693
|
+
|
|
4694
|
+
Object.assign( FBXTree.prototype, {
|
|
4695
|
+
|
|
4696
|
+
add: function ( key, val ) {
|
|
4697
|
+
|
|
4698
|
+
this[ key ] = val;
|
|
4699
|
+
|
|
4700
|
+
},
|
|
4701
|
+
|
|
4702
|
+
searchConnectionParent: function ( id ) {
|
|
4703
|
+
|
|
4704
|
+
if ( this.__cache_search_connection_parent === undefined ) {
|
|
4705
|
+
|
|
4706
|
+
this.__cache_search_connection_parent = [];
|
|
4707
|
+
|
|
4708
|
+
}
|
|
4709
|
+
|
|
4710
|
+
if ( this.__cache_search_connection_parent[ id ] !== undefined ) {
|
|
4711
|
+
|
|
4712
|
+
return this.__cache_search_connection_parent[ id ];
|
|
4713
|
+
|
|
4714
|
+
} else {
|
|
4715
|
+
|
|
4716
|
+
this.__cache_search_connection_parent[ id ] = [];
|
|
4717
|
+
|
|
4718
|
+
}
|
|
4719
|
+
|
|
4720
|
+
var conns = this.Connections.properties.connections;
|
|
4721
|
+
|
|
4722
|
+
var results = [];
|
|
4723
|
+
for ( var i = 0; i < conns.length; ++ i ) {
|
|
4724
|
+
|
|
4725
|
+
if ( conns[ i ][ 0 ] == id ) {
|
|
4726
|
+
|
|
4727
|
+
// 0 means scene root
|
|
4728
|
+
var res = conns[ i ][ 1 ] === 0 ? - 1 : conns[ i ][ 1 ];
|
|
4729
|
+
results.push( res );
|
|
4730
|
+
|
|
4731
|
+
}
|
|
4732
|
+
|
|
4733
|
+
}
|
|
4734
|
+
|
|
4735
|
+
if ( results.length > 0 ) {
|
|
4736
|
+
|
|
4737
|
+
append( this.__cache_search_connection_parent[ id ], results );
|
|
4738
|
+
return results;
|
|
4739
|
+
|
|
4740
|
+
} else {
|
|
4741
|
+
|
|
4742
|
+
this.__cache_search_connection_parent[ id ] = [ - 1 ];
|
|
4743
|
+
return [ - 1 ];
|
|
4744
|
+
|
|
4745
|
+
}
|
|
4746
|
+
|
|
4747
|
+
},
|
|
4748
|
+
|
|
4749
|
+
searchConnectionChildren: function ( id ) {
|
|
4750
|
+
|
|
4751
|
+
if ( this.__cache_search_connection_children === undefined ) {
|
|
4752
|
+
|
|
4753
|
+
this.__cache_search_connection_children = [];
|
|
4754
|
+
|
|
4755
|
+
}
|
|
4756
|
+
|
|
4757
|
+
if ( this.__cache_search_connection_children[ id ] !== undefined ) {
|
|
4758
|
+
|
|
4759
|
+
return this.__cache_search_connection_children[ id ];
|
|
4760
|
+
|
|
4761
|
+
} else {
|
|
4762
|
+
|
|
4763
|
+
this.__cache_search_connection_children[ id ] = [];
|
|
4764
|
+
|
|
4765
|
+
}
|
|
4766
|
+
|
|
4767
|
+
var conns = this.Connections.properties.connections;
|
|
4768
|
+
|
|
4769
|
+
var res = [];
|
|
4770
|
+
for ( var i = 0; i < conns.length; ++ i ) {
|
|
4771
|
+
|
|
4772
|
+
if ( conns[ i ][ 1 ] == id ) {
|
|
4773
|
+
|
|
4774
|
+
// 0 means scene root
|
|
4775
|
+
res.push( conns[ i ][ 0 ] === 0 ? - 1 : conns[ i ][ 0 ] );
|
|
4776
|
+
// there may more than one kid, then search to the end
|
|
4777
|
+
|
|
4778
|
+
}
|
|
4779
|
+
|
|
4780
|
+
}
|
|
4781
|
+
|
|
4782
|
+
if ( res.length > 0 ) {
|
|
4783
|
+
|
|
4784
|
+
append( this.__cache_search_connection_children[ id ], res );
|
|
4785
|
+
return res;
|
|
4786
|
+
|
|
4787
|
+
} else {
|
|
4788
|
+
|
|
4789
|
+
this.__cache_search_connection_children[ id ] = [ ];
|
|
4790
|
+
return [ ];
|
|
4791
|
+
|
|
4792
|
+
}
|
|
4793
|
+
|
|
4794
|
+
},
|
|
4795
|
+
|
|
4796
|
+
searchConnectionType: function ( id, to ) {
|
|
4797
|
+
|
|
4798
|
+
var key = id + ',' + to; // TODO: to hash
|
|
4799
|
+
if ( this.__cache_search_connection_type === undefined ) {
|
|
4800
|
+
|
|
4801
|
+
this.__cache_search_connection_type = {};
|
|
4802
|
+
|
|
4803
|
+
}
|
|
4804
|
+
|
|
4805
|
+
if ( this.__cache_search_connection_type[ key ] !== undefined ) {
|
|
4806
|
+
|
|
4807
|
+
return this.__cache_search_connection_type[ key ];
|
|
4808
|
+
|
|
4809
|
+
} else {
|
|
4810
|
+
|
|
4811
|
+
this.__cache_search_connection_type[ key ] = '';
|
|
4812
|
+
|
|
4813
|
+
}
|
|
4814
|
+
|
|
4815
|
+
var conns = this.Connections.properties.connections;
|
|
4816
|
+
|
|
4817
|
+
for ( var i = 0; i < conns.length; ++ i ) {
|
|
4818
|
+
|
|
4819
|
+
if ( conns[ i ][ 0 ] == id && conns[ i ][ 1 ] == to ) {
|
|
4820
|
+
|
|
4821
|
+
// 0 means scene root
|
|
4822
|
+
this.__cache_search_connection_type[ key ] = conns[ i ][ 2 ];
|
|
4823
|
+
return conns[ i ][ 2 ];
|
|
4824
|
+
|
|
4825
|
+
}
|
|
4826
|
+
|
|
4827
|
+
}
|
|
4828
|
+
|
|
4829
|
+
this.__cache_search_connection_type[ id ] = null;
|
|
4830
|
+
return null;
|
|
4831
|
+
|
|
4832
|
+
}
|
|
4833
|
+
|
|
4834
|
+
} );
|
|
4835
|
+
|
|
4836
|
+
|
|
4837
|
+
/**
|
|
4838
|
+
* @param {ArrayBuffer} buffer
|
|
4839
|
+
* @returns {boolean}
|
|
4840
|
+
*/
|
|
4841
|
+
function isFbxFormatBinary( buffer ) {
|
|
4842
|
+
|
|
4843
|
+
var CORRECT = 'Kaydara FBX Binary \0';
|
|
4844
|
+
|
|
4845
|
+
return buffer.byteLength >= CORRECT.length && CORRECT === convertArrayBufferToString( buffer, 0, CORRECT.length );
|
|
4846
|
+
|
|
4847
|
+
}
|
|
4848
|
+
|
|
4849
|
+
/**
|
|
4850
|
+
* @returns {boolean}
|
|
4851
|
+
*/
|
|
4852
|
+
function isFbxFormatASCII( text ) {
|
|
4853
|
+
|
|
4854
|
+
var CORRECT = [ 'K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\' ];
|
|
4855
|
+
|
|
4856
|
+
var cursor = 0;
|
|
4857
|
+
|
|
4858
|
+
function read( offset ) {
|
|
4859
|
+
|
|
4860
|
+
var result = text[ offset - 1 ];
|
|
4861
|
+
text = text.slice( cursor + offset );
|
|
4862
|
+
cursor ++;
|
|
4863
|
+
return result;
|
|
4864
|
+
|
|
4865
|
+
}
|
|
4866
|
+
|
|
4867
|
+
for ( var i = 0; i < CORRECT.length; ++ i ) {
|
|
4868
|
+
|
|
4869
|
+
var num = read( 1 );
|
|
4870
|
+
if ( num == CORRECT[ i ] ) {
|
|
4871
|
+
|
|
4872
|
+
return false;
|
|
4873
|
+
|
|
4874
|
+
}
|
|
4875
|
+
|
|
4876
|
+
}
|
|
4877
|
+
|
|
4878
|
+
return true;
|
|
4879
|
+
|
|
4880
|
+
}
|
|
4881
|
+
|
|
4882
|
+
/**
|
|
4883
|
+
* @returns {number}
|
|
4884
|
+
*/
|
|
4885
|
+
function getFbxVersion( text ) {
|
|
4886
|
+
|
|
4887
|
+
var versionRegExp = /FBXVersion: (\d+)/;
|
|
4888
|
+
var match = text.match( versionRegExp );
|
|
4889
|
+
if ( match ) {
|
|
4890
|
+
|
|
4891
|
+
var version = parseInt( match[ 1 ] );
|
|
4892
|
+
return version;
|
|
4893
|
+
|
|
4894
|
+
}
|
|
4895
|
+
throw new Error( 'FBXLoader: Cannot find the version number for the file given.' );
|
|
4896
|
+
|
|
4897
|
+
}
|
|
4898
|
+
|
|
4899
|
+
/**
|
|
4900
|
+
* Converts FBX ticks into real time seconds.
|
|
4901
|
+
* @param {number} time - FBX tick timestamp to convert.
|
|
4902
|
+
* @returns {number} - FBX tick in real world time.
|
|
4903
|
+
*/
|
|
4904
|
+
function convertFBXTimeToSeconds( time ) {
|
|
4905
|
+
|
|
4906
|
+
// Constant is FBX ticks per second.
|
|
4907
|
+
return time / 46186158000;
|
|
4908
|
+
|
|
4909
|
+
}
|
|
4910
|
+
|
|
4911
|
+
/**
|
|
4912
|
+
* Parses comma separated list of float numbers and returns them in an array.
|
|
4913
|
+
* @example
|
|
4914
|
+
* // Returns [ 5.6, 9.4, 2.5, 1.4 ]
|
|
4915
|
+
* parseFloatArray( "5.6,9.4,2.5,1.4" )
|
|
4916
|
+
* @returns {number[]}
|
|
4917
|
+
*/
|
|
4918
|
+
function parseFloatArray( string ) {
|
|
4919
|
+
|
|
4920
|
+
var array = string.split( ',' );
|
|
4921
|
+
|
|
4922
|
+
for ( var i = 0, l = array.length; i < l; i ++ ) {
|
|
4923
|
+
|
|
4924
|
+
array[ i ] = parseFloat( array[ i ] );
|
|
4925
|
+
|
|
4926
|
+
}
|
|
4927
|
+
|
|
4928
|
+
return array;
|
|
4929
|
+
|
|
4930
|
+
}
|
|
4931
|
+
|
|
4932
|
+
/**
|
|
4933
|
+
* Parses comma separated list of int numbers and returns them in an array.
|
|
4934
|
+
* @example
|
|
4935
|
+
* // Returns [ 5, 8, 2, 3 ]
|
|
4936
|
+
* parseFloatArray( "5,8,2,3" )
|
|
4937
|
+
* @returns {number[]}
|
|
4938
|
+
*/
|
|
4939
|
+
function parseIntArray( string ) {
|
|
4940
|
+
|
|
4941
|
+
var array = string.split( ',' );
|
|
4942
|
+
|
|
4943
|
+
for ( var i = 0, l = array.length; i < l; i ++ ) {
|
|
4944
|
+
|
|
4945
|
+
array[ i ] = parseInt( array[ i ] );
|
|
4946
|
+
|
|
4947
|
+
}
|
|
4948
|
+
|
|
4949
|
+
return array;
|
|
4950
|
+
|
|
4951
|
+
}
|
|
4952
|
+
|
|
4953
|
+
/**
|
|
4954
|
+
* Parses Vector3 property from FBXTree. Property is given as .value.x, .value.y, etc.
|
|
4955
|
+
* @param {FBXVector3} property - Property to parse as Vector3.
|
|
4956
|
+
* @returns {THREE.Vector3}
|
|
4957
|
+
*/
|
|
4958
|
+
function parseVector3( property ) {
|
|
4959
|
+
|
|
4960
|
+
return new THREE.Vector3().fromArray( property.value );
|
|
4961
|
+
|
|
4962
|
+
}
|
|
4963
|
+
|
|
4964
|
+
/**
|
|
4965
|
+
* Parses Color property from FBXTree. Property is given as .value.x, .value.y, etc.
|
|
4966
|
+
* @param {FBXVector3} property - Property to parse as Color.
|
|
4967
|
+
* @returns {THREE.Color}
|
|
4968
|
+
*/
|
|
4969
|
+
function parseColor( property ) {
|
|
4970
|
+
|
|
4971
|
+
return new THREE.Color().fromArray( property.value );
|
|
4972
|
+
|
|
4973
|
+
}
|
|
4974
|
+
|
|
4975
|
+
function parseMatrixArray( floatString ) {
|
|
4976
|
+
|
|
4977
|
+
return new THREE.Matrix4().fromArray( parseFloatArray( floatString ) );
|
|
4978
|
+
|
|
4979
|
+
}
|
|
4980
|
+
|
|
4981
|
+
/**
|
|
4982
|
+
* Converts ArrayBuffer to String.
|
|
4983
|
+
* @param {ArrayBuffer} buffer
|
|
4984
|
+
* @param {number} from
|
|
4985
|
+
* @param {number} to
|
|
4986
|
+
* @returns {String}
|
|
4987
|
+
*/
|
|
4988
|
+
function convertArrayBufferToString( buffer, from, to ) {
|
|
4989
|
+
|
|
4990
|
+
if ( from === undefined ) from = 0;
|
|
4991
|
+
if ( to === undefined ) to = buffer.byteLength;
|
|
4992
|
+
|
|
4993
|
+
var array = new Uint8Array( buffer, from, to );
|
|
4994
|
+
|
|
4995
|
+
if ( self.TextDecoder !== undefined ) {
|
|
4996
|
+
|
|
4997
|
+
return new TextDecoder().decode( array );
|
|
4998
|
+
|
|
4999
|
+
}
|
|
5000
|
+
|
|
5001
|
+
|
|
5002
|
+
var s = '';
|
|
5003
|
+
|
|
5004
|
+
for ( var i = 0, il = array.length; i < il; i ++ ) {
|
|
5005
|
+
|
|
5006
|
+
s += String.fromCharCode( array[ i ] );
|
|
5007
|
+
|
|
5008
|
+
}
|
|
5009
|
+
|
|
5010
|
+
return s;
|
|
5011
|
+
|
|
5012
|
+
}
|
|
5013
|
+
|
|
5014
|
+
/**
|
|
5015
|
+
* Converts number from degrees into radians.
|
|
5016
|
+
* @param {number} value
|
|
5017
|
+
* @returns {number}
|
|
5018
|
+
*/
|
|
5019
|
+
function degreeToRadian( value ) {
|
|
5020
|
+
|
|
5021
|
+
return value * DEG2RAD;
|
|
5022
|
+
|
|
5023
|
+
}
|
|
5024
|
+
|
|
5025
|
+
var DEG2RAD = Math.PI / 180;
|
|
5026
|
+
|
|
5027
|
+
//
|
|
5028
|
+
|
|
5029
|
+
function findIndex( array, func ) {
|
|
5030
|
+
|
|
5031
|
+
for ( var i = 0, l = array.length; i < l; i ++ ) {
|
|
5032
|
+
|
|
5033
|
+
if ( func( array[ i ] ) ) return i;
|
|
5034
|
+
|
|
5035
|
+
}
|
|
5036
|
+
|
|
5037
|
+
return -1;
|
|
5038
|
+
|
|
5039
|
+
}
|
|
5040
|
+
|
|
5041
|
+
function append( a, b ) {
|
|
5042
|
+
|
|
5043
|
+
for ( var i = 0, j = a.length, l = b.length; i < l; i ++, j ++ ) {
|
|
5044
|
+
|
|
5045
|
+
a[ j ] = b[ i ];
|
|
5046
|
+
|
|
5047
|
+
}
|
|
5048
|
+
|
|
5049
|
+
}
|
|
5050
|
+
|
|
5051
|
+
function slice( a, b, from, to ) {
|
|
5052
|
+
|
|
5053
|
+
for ( var i = from, j = 0; i < to; i ++, j ++ ) {
|
|
5054
|
+
|
|
5055
|
+
a[ j ] = b[ i ];
|
|
5056
|
+
|
|
5057
|
+
}
|
|
5058
|
+
|
|
5059
|
+
return a;
|
|
5060
|
+
|
|
5061
|
+
}
|
|
5062
|
+
|
|
5063
|
+
} )();
|