|
@@ -39,6 +39,13 @@ var THREE_TO_WEBGL = {
|
|
|
1008: WEBGL_CONSTANTS.LINEAR_MIPMAP_LINEAR
|
|
|
};
|
|
|
|
|
|
+var PATH_PROPERTIES = {
|
|
|
+ scale: 'scale',
|
|
|
+ position: 'translation',
|
|
|
+ quaternion: 'rotation',
|
|
|
+ morphTargetInfluences: 'weights'
|
|
|
+};
|
|
|
+
|
|
|
//------------------------------------------------------------------------------
|
|
|
// GLTF Exporter
|
|
|
//------------------------------------------------------------------------------
|
|
@@ -53,8 +60,6 @@ THREE.GLTFExporter.prototype = {
|
|
|
* @param {THREE.Scene or [THREE.Scenes]} input THREE.Scene or Array of THREE.Scenes
|
|
|
* @param {Function} onDone Callback on completed
|
|
|
* @param {Object} options options
|
|
|
- * trs: Exports position, rotation and scale instead of matrix
|
|
|
- * binary: Exports `.glb` as ArrayBuffer, instead of `.gltf` as JSON
|
|
|
*/
|
|
|
parse: function ( input, onDone, options ) {
|
|
|
|
|
@@ -62,7 +67,8 @@ THREE.GLTFExporter.prototype = {
|
|
|
trs: false,
|
|
|
onlyVisible: true,
|
|
|
truncateDrawRange: true,
|
|
|
- embedImages: true
|
|
|
+ embedImages: true,
|
|
|
+ animations: []
|
|
|
};
|
|
|
|
|
|
options = Object.assign( {}, DEFAULT_OPTIONS, options );
|
|
@@ -80,6 +86,7 @@ THREE.GLTFExporter.prototype = {
|
|
|
|
|
|
var byteOffset = 0;
|
|
|
var dataViews = [];
|
|
|
+ var nodeMap = {};
|
|
|
var cachedData = {
|
|
|
|
|
|
images: {},
|
|
@@ -136,8 +143,8 @@ THREE.GLTFExporter.prototype = {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Get the min and he max vectors from the given attribute
|
|
|
- * @param {THREE.WebGLAttribute} attribute Attribute to find the min/max
|
|
|
+ * Get the min and max vectors from the given attribute
|
|
|
+ * @param {THREE.BufferAttribute} attribute Attribute to find the min/max
|
|
|
* @return {Object} Object containing the `min` and `max` values (As an array of attribute.itemSize components)
|
|
|
*/
|
|
|
function getMinMax( attribute ) {
|
|
@@ -232,12 +239,14 @@ THREE.GLTFExporter.prototype = {
|
|
|
|
|
|
/**
|
|
|
* Process and generate a BufferView
|
|
|
- * @param {[type]} data [description]
|
|
|
- * @return {[type]} [description]
|
|
|
+ * @param {THREE.BufferAttribute} data
|
|
|
+ * @param {number} componentType
|
|
|
+ * @param {number} start
|
|
|
+ * @param {number} count
|
|
|
+ * @param {number} target (Optional) Target usage of the BufferView
|
|
|
+ * @return {Object}
|
|
|
*/
|
|
|
- function processBufferView( data, componentType, start, count ) {
|
|
|
-
|
|
|
- var isVertexAttributes = componentType === WEBGL_CONSTANTS.FLOAT;
|
|
|
+ function processBufferView( data, componentType, start, count, target ) {
|
|
|
|
|
|
if ( ! outputJSON.bufferViews ) {
|
|
|
|
|
@@ -255,11 +264,12 @@ THREE.GLTFExporter.prototype = {
|
|
|
buffer: processBuffer( data, componentType, start, count ),
|
|
|
byteOffset: byteOffset,
|
|
|
byteLength: byteLength,
|
|
|
- byteStride: data.itemSize * componentSize,
|
|
|
- target: isVertexAttributes ? WEBGL_CONSTANTS.ARRAY_BUFFER : WEBGL_CONSTANTS.ELEMENT_ARRAY_BUFFER
|
|
|
+ byteStride: data.itemSize * componentSize
|
|
|
|
|
|
};
|
|
|
|
|
|
+ if ( target !== undefined ) gltfBufferView.target = target;
|
|
|
+
|
|
|
byteOffset += byteLength;
|
|
|
|
|
|
outputJSON.bufferViews.push( gltfBufferView );
|
|
@@ -278,7 +288,8 @@ THREE.GLTFExporter.prototype = {
|
|
|
|
|
|
/**
|
|
|
* Process attribute to generate an accessor
|
|
|
- * @param {THREE.WebGLAttribute} attribute Attribute to process
|
|
|
+ * @param {THREE.BufferAttribute} attribute Attribute to process
|
|
|
+ * @param {THREE.BufferGeometry} geometry (Optional) Geometry used for truncated draw range
|
|
|
* @return {Integer} Index of the processed accessor on the "accessors" array
|
|
|
*/
|
|
|
function processAccessor( attribute, geometry ) {
|
|
@@ -325,14 +336,25 @@ THREE.GLTFExporter.prototype = {
|
|
|
var count = attribute.count;
|
|
|
|
|
|
// @TODO Indexed buffer geometry with drawRange not supported yet
|
|
|
- if ( options.truncateDrawRange && geometry.index === null ) {
|
|
|
+ if ( options.truncateDrawRange && geometry !== undefined && geometry.index === null ) {
|
|
|
|
|
|
start = geometry.drawRange.start;
|
|
|
count = geometry.drawRange.count !== Infinity ? geometry.drawRange.count : attribute.count;
|
|
|
|
|
|
}
|
|
|
|
|
|
- var bufferView = processBufferView( attribute, componentType, start, count );
|
|
|
+ var bufferViewTarget;
|
|
|
+
|
|
|
+ // If geometry isn't provided, don't infer the target usage of the bufferView. For
|
|
|
+ // animation samplers, target must not be set.
|
|
|
+ if ( geometry !== undefined ) {
|
|
|
+
|
|
|
+ var isVertexAttributes = componentType === WEBGL_CONSTANTS.FLOAT;
|
|
|
+ bufferViewTarget = isVertexAttributes ? WEBGL_CONSTANTS.ARRAY_BUFFER : WEBGL_CONSTANTS.ELEMENT_ARRAY_BUFFER;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var bufferView = processBufferView( attribute, componentType, start, count, bufferViewTarget );
|
|
|
|
|
|
var gltfAccessor = {
|
|
|
|
|
@@ -829,6 +851,73 @@ THREE.GLTFExporter.prototype = {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Creates glTF animation entry from AnimationClip object.
|
|
|
+ *
|
|
|
+ * Status:
|
|
|
+ * - Only properties listed in PATH_PROPERTIES may be animated.
|
|
|
+ * - Only LINEAR and STEP interpolation currently supported.
|
|
|
+ *
|
|
|
+ * @param {THREE.AnimationClip} clip
|
|
|
+ * @param {THREE.Object3D} root
|
|
|
+ * @return {number}
|
|
|
+ */
|
|
|
+ function processAnimation ( clip, root ) {
|
|
|
+
|
|
|
+ if ( ! outputJSON.animations ) {
|
|
|
+
|
|
|
+ outputJSON.animations = [];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var channels = [];
|
|
|
+ var samplers = [];
|
|
|
+
|
|
|
+ for ( var i = 0; i < clip.tracks.length; ++ i ) {
|
|
|
+
|
|
|
+ var track = clip.tracks[ i ];
|
|
|
+ var trackBinding = THREE.PropertyBinding.parseTrackName( track.name );
|
|
|
+ var trackNode = THREE.PropertyBinding.findNode( root, trackBinding.nodeName );
|
|
|
+ var trackProperty = PATH_PROPERTIES[ trackBinding.propertyName ];
|
|
|
+
|
|
|
+ if ( ! trackNode || ! trackProperty ) {
|
|
|
+
|
|
|
+ console.warn( 'THREE.GLTFExporter: Could not export animation track "%s".', track.name );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ samplers.push( {
|
|
|
+
|
|
|
+ input: processAccessor( new THREE.BufferAttribute( track.times, 1 ) ),
|
|
|
+ output: processAccessor( new THREE.BufferAttribute( track.values, 1 ) ),
|
|
|
+ interpolation: track.interpolation === THREE.InterpolateDiscrete ? 'STEP' : 'LINEAR'
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ channels.push( {
|
|
|
+
|
|
|
+ sampler: samplers.length - 1,
|
|
|
+ target: {
|
|
|
+ node: nodeMap[ trackNode.uuid ],
|
|
|
+ path: trackProperty
|
|
|
+ }
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ outputJSON.animations.push( {
|
|
|
+
|
|
|
+ name: clip.name || 'clip_' + outputJSON.animations.length,
|
|
|
+ samplers: samplers,
|
|
|
+ channels: channels
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ return outputJSON.animations.length - 1;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Process Object3D node
|
|
|
* @param {THREE.Object3D} node Object3D to processNode
|
|
@@ -995,6 +1084,7 @@ THREE.GLTFExporter.prototype = {
|
|
|
if ( node !== null ) {
|
|
|
|
|
|
nodes.push( node );
|
|
|
+ nodeMap[ child.uuid ] = node;
|
|
|
|
|
|
}
|
|
|
|
|
@@ -1057,6 +1147,12 @@ THREE.GLTFExporter.prototype = {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ for ( var i = 0; i < options.animations.length; ++ i ) {
|
|
|
+
|
|
|
+ processAnimation( options.animations[ i ], input[ 0 ] );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
processInput( input );
|