diff --git a/examples/webgl_collada.html b/examples/webgl_collada.html
index 6e370e4c..c63a44ef 100644
--- a/examples/webgl_collada.html
+++ b/examples/webgl_collada.html
@@ -31,13 +31,13 @@
var dae, skin;
var loader = new THREE.ColladaLoader();
+ loader.options.convertUpAxis = true;
loader.load( './models/monster.dae', function colladaReady( collada ) {
dae = collada.scene;
skin = collada.skins[ 0 ];
dae.scale.x = dae.scale.y = dae.scale.z = 0.002;
- dae.rotation.x = -Math.PI/2;
dae.updateMatrix();
init();
diff --git a/src/extras/animation/Animation.js b/src/extras/animation/Animation.js
index 1e51b87b..2caa337b 100644
--- a/src/extras/animation/Animation.js
+++ b/src/extras/animation/Animation.js
@@ -10,7 +10,7 @@ THREE.Animation = function( root, data, interpolationType, JITCompile ) {
this.data = THREE.AnimationHandler.get( data );
this.hierarchy = THREE.AnimationHandler.parse( root );
this.currentTime = 0;
- this.timeScale = 1;
+ this.timeScale = 0.001;
this.isPlaying = false;
this.isPaused = true;
this.loop = true;
@@ -20,6 +20,37 @@ THREE.Animation = function( root, data, interpolationType, JITCompile ) {
this.points = [];
this.target = new THREE.Vector3();
+ // initialize to first keyframes
+
+ for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+
+ var keys = this.data.hierarchy[h].keys,
+ sids = this.data.hierarchy[h].sids,
+ obj = this.hierarchy[h];
+
+ if ( keys.length ) {
+
+ for ( var s = 0; s < sids.length; s++ ) {
+
+ var sid = sids[ s ],
+ next = this.getNextKeyWith( sid, h, 0 );
+
+ if ( next ) {
+
+ next.apply( sid );
+
+ }
+
+ }
+
+ obj.matrixAutoUpdate = false;
+ this.data.hierarchy[h].node.updateMatrix();
+ obj.matrixWorldNeedsUpdate = true;
+
+ }
+
+ }
+
};
// Play
@@ -31,16 +62,21 @@ THREE.Animation.prototype.play = function( loop, startTimeMS ) {
this.isPlaying = true;
this.loop = loop !== undefined ? loop : true;
this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
+ this.startTimeMs = startTimeMS;
+ this.startTime = 10000000;
+ this.endTime = -this.startTime;
// reset key cache
var h, hl = this.hierarchy.length,
- object;
+ object,
+ node;
for ( h = 0; h < hl; h++ ) {
object = this.hierarchy[ h ];
+ node = this.data.hierarchy[ h ];
if ( this.interpolationType !== THREE.AnimationHandler.CATMULLROM_FORWARD ) {
@@ -48,27 +84,26 @@ THREE.Animation.prototype.play = function( loop, startTimeMS ) {
}
- object.matrixAutoUpdate = true;
+ if ( node.animationCache === undefined ) {
- if ( object.animationCache === undefined ) {
-
- object.animationCache = {};
- object.animationCache.prevKey = { pos: 0, rot: 0, scl: 0 };
- object.animationCache.nextKey = { pos: 0, rot: 0, scl: 0 };
- object.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
+ node.animationCache = {};
+ node.animationCache.prevKey = null;
+ node.animationCache.nextKey = null;
+ node.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
}
- var prevKey = object.animationCache.prevKey;
- var nextKey = object.animationCache.nextKey;
+ var keys = this.data.hierarchy[h].keys;
- prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ];
- prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ];
- prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ];
+ if (keys.length) {
- nextKey.pos = this.getNextKeyWith( "pos", h, 1 );
- nextKey.rot = this.getNextKeyWith( "rot", h, 1 );
- nextKey.scl = this.getNextKeyWith( "scl", h, 1 );
+ node.animationCache.prevKey = keys[ 0 ];
+ node.animationCache.nextKey = keys[ 1 ];
+
+ this.startTime = Math.min( keys[0].time, this.startTime );
+ this.endTime = Math.max( keys[keys.length - 1].time, this.endTime );
+
+ }
}
@@ -116,20 +151,25 @@ THREE.Animation.prototype.stop = function() {
for ( var h = 0; h < this.hierarchy.length; h++ ) {
- if ( this.hierarchy[ h ].animationCache !== undefined ) {
+ var obj = this.hierarchy[ h ];
- if( this.hierarchy[ h ] instanceof THREE.Bone ) {
+ if ( obj.animationCache !== undefined ) {
- this.hierarchy[ h ].skinMatrix = this.hierarchy[ h ].animationCache.originalMatrix;
+ var original = obj.animationCache.originalMatrix;
+
+ if( obj instanceof THREE.Bone ) {
+
+ original.copy( obj.skinMatrix );
+ obj.skinMatrix = original;
} else {
- this.hierarchy[ h ].matrix = this.hierarchy[ h ].animationCache.originalMatrix;
+ original.copy( obj.matrix );
+ obj.matrix = original;
}
-
- delete this.hierarchy[ h ].animationCache;
+ delete obj.animationCache;
}
@@ -149,18 +189,13 @@ THREE.Animation.prototype.update = function( deltaTimeMS ) {
// vars
- var types = [ "pos", "rot", "scl" ];
- var type;
- var scale;
- var vector;
- var prevXYZ, nextXYZ;
var prevKey, nextKey;
var object;
- var animationCache;
+ var node;
var frame;
var JIThierarchy = this.data.JIT.hierarchy;
var currentTime, unloopedCurrentTime;
- var currentPoint, forwardPoint, angle;
+ var looped;
// update
@@ -169,15 +204,70 @@ THREE.Animation.prototype.update = function( deltaTimeMS ) {
unloopedCurrentTime = this.currentTime;
currentTime = this.currentTime = this.currentTime % this.data.length;
- frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
+ // if looped around, the current time should be based on the startTime
+ if ( currentTime < this.startTimeMs ) {
+
+ currentTime = this.currentTime = this.startTimeMs + currentTime;
+
+ }
+
+ frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
+ looped = currentTime < unloopedCurrentTime;
+
+ if ( looped && !this.loop ) {
+
+ // Set the animation to the last keyframes and stop
+ for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+
+ var keys = this.data.hierarchy[h].keys,
+ sids = this.data.hierarchy[h].sids,
+ end = keys.length-1,
+ obj = this.hierarchy[h];
+
+ if ( keys.length ) {
+
+ for ( var s = 0; s < sids.length; s++ ) {
+
+ var sid = sids[ s ],
+ prev = this.getPrevKeyWith( sid, h, end );
+
+ if ( prev ) {
+
+ prev.apply( sid );
+
+ }
+
+ }
+
+ this.data.hierarchy[h].node.updateMatrix();
+ obj.matrixWorldNeedsUpdate = true;
+
+ }
+
+ }
+
+ this.stop();
+ return;
+
+ }
+
+ // check pre-infinity
+ if ( currentTime < this.startTime ) {
+
+ return;
+
+ }
// update
for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
object = this.hierarchy[ h ];
- animationCache = object.animationCache;
+ node = this.data.hierarchy[ h ];
+
+ var keys = node.keys,
+ animationCache = node.animationCache;
// use JIT?
@@ -186,49 +276,39 @@ THREE.Animation.prototype.update = function( deltaTimeMS ) {
if( object instanceof THREE.Bone ) {
object.skinMatrix = JIThierarchy[ h ][ frame ];
-
- object.matrixAutoUpdate = false;
object.matrixWorldNeedsUpdate = false;
} else {
object.matrix = JIThierarchy[ h ][ frame ];
-
- object.matrixAutoUpdate = false;
object.matrixWorldNeedsUpdate = true;
}
// use interpolation
- } else {
+ } else if ( keys.length ) {
// make sure so original matrix and not JIT matrix is set
- if ( this.JITCompile ) {
+ if ( this.JITCompile && animationCache ) {
if( object instanceof THREE.Bone ) {
- object.skinMatrix = object.animationCache.originalMatrix;
+ object.skinMatrix = animationCache.originalMatrix;
} else {
- object.matrix = object.animationCache.originalMatrix;
+ object.matrix = animationCache.originalMatrix;
}
}
+ prevKey = animationCache.prevKey;
+ nextKey = animationCache.nextKey;
- // loop through pos/rot/scl
-
- for ( var t = 0; t < 3; t++ ) {
-
- // get keys
-
- type = types[ t ];
- prevKey = animationCache.prevKey[ type ];
- nextKey = animationCache.nextKey[ type ];
+ if ( prevKey && nextKey ) {
// switch keys?
@@ -236,121 +316,43 @@ THREE.Animation.prototype.update = function( deltaTimeMS ) {
// did we loop?
- if ( currentTime < unloopedCurrentTime ) {
+ if ( looped && this.loop ) {
- if ( this.loop ) {
+ prevKey = keys[ 0 ];
+ nextKey = keys[ 1 ];
- prevKey = this.data.hierarchy[ h ].keys[ 0 ];
- nextKey = this.getNextKeyWith( type, h, 1 );
-
- while( nextKey.time < currentTime ) {
-
- prevKey = nextKey;
- nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
-
- }
-
- } else {
-
- this.stop();
- return;
-
- }
-
- } else {
-
- do {
+ while ( nextKey.time < currentTime ) {
prevKey = nextKey;
- nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
+ nextKey = keys[ prevKey.index + 1 ];
- } while( nextKey.time < currentTime )
+ }
- }
+ } else if ( !looped ) {
- animationCache.prevKey[ type ] = prevKey;
- animationCache.nextKey[ type ] = nextKey;
+ var lastIndex = keys.length - 1;
- }
+ while ( nextKey.time < currentTime && nextKey.index !== lastIndex ) {
-
- object.matrixAutoUpdate = true;
- object.matrixWorldNeedsUpdate = true;
-
- scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
- prevXYZ = prevKey[ type ];
- nextXYZ = nextKey[ type ];
-
-
- // check scale error
-
- if ( scale < 0 || scale > 1 ) {
-
- console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale + " on bone " + h );
- scale = scale < 0 ? 0 : 1;
-
- }
-
- // interpolate
-
- if ( type === "pos" ) {
-
- vector = object.position;
-
- if( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
-
- vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
- vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
- vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
-
- } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
- this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
-
- this.points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
- this.points[ 1 ] = prevXYZ;
- this.points[ 2 ] = nextXYZ;
- this.points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
-
- scale = scale * 0.33 + 0.33;
-
- currentPoint = this.interpolateCatmullRom( this.points, scale );
-
- vector.x = currentPoint[ 0 ];
- vector.y = currentPoint[ 1 ];
- vector.z = currentPoint[ 2 ];
-
- if( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
-
- forwardPoint = this.interpolateCatmullRom( this.points, scale * 1.01 );
-
- this.target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
- this.target.subSelf( vector );
- this.target.y = 0;
- this.target.normalize();
-
- angle = Math.atan2( this.target.x, this.target.z );
- object.rotation.set( 0, angle, 0 );
+ prevKey = nextKey;
+ nextKey = keys[ prevKey.index + 1 ];
}
}
- } else if ( type === "rot" ) {
-
- THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale );
-
- } else if( type === "scl" ) {
-
- vector = object.scale;
-
- vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
- vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
- vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
+ animationCache.prevKey = prevKey;
+ animationCache.nextKey = nextKey;
}
+ prevKey.interpolate( nextKey, currentTime );
+
}
+ this.data.hierarchy[h].node.updateMatrix();
+ object.matrixWorldNeedsUpdate = true;
+
}
}
@@ -429,7 +431,7 @@ THREE.Animation.prototype.interpolate = function( p0, p1, p2, p3, t, t2, t3 ) {
// Get next key with
-THREE.Animation.prototype.getNextKeyWith = function( type, h, key ) {
+THREE.Animation.prototype.getNextKeyWith = function( sid, h, key ) {
var keys = this.data.hierarchy[ h ].keys;
@@ -446,7 +448,7 @@ THREE.Animation.prototype.getNextKeyWith = function( type, h, key ) {
for ( ; key < keys.length; key++ ) {
- if ( keys[ key ][ type ] !== undefined ) {
+ if ( keys[ key ].hasTarget( sid ) ) {
return keys[ key ];
@@ -454,13 +456,13 @@ THREE.Animation.prototype.getNextKeyWith = function( type, h, key ) {
}
- return this.data.hierarchy[ h ].keys[ 0 ];
+ return keys[ 0 ];
};
// Get previous key with
-THREE.Animation.prototype.getPrevKeyWith = function( type, h, key ) {
+THREE.Animation.prototype.getPrevKeyWith = function( sid, h, key ) {
var keys = this.data.hierarchy[ h ].keys;
@@ -478,7 +480,7 @@ THREE.Animation.prototype.getPrevKeyWith = function( type, h, key ) {
for ( ; key >= 0; key-- ) {
- if ( keys[ key ][ type ] !== undefined ) {
+ if ( keys[ key ].hasTarget( sid ) ) {
return keys[ key ];
@@ -486,6 +488,6 @@ THREE.Animation.prototype.getPrevKeyWith = function( type, h, key ) {
}
- return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
+ return keys[ keys.length - 1 ];
};
diff --git a/src/extras/animation/AnimationHandler.js b/src/extras/animation/AnimationHandler.js
index 92965694..7f1a622c 100644
--- a/src/extras/animation/AnimationHandler.js
+++ b/src/extras/animation/AnimationHandler.js
@@ -149,7 +149,7 @@ THREE.AnimationHandler = (function() {
// prepare morph target keys
- if( data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
+ if( data.hierarchy[h].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
// get all used
@@ -219,7 +219,7 @@ THREE.AnimationHandler = (function() {
// set index
- for( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) {
+ for( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
data.hierarchy[ h ].keys[ k ].index = k;
diff --git a/src/extras/loaders/ColladaLoader.js b/src/extras/loaders/ColladaLoader.js
index fd288410..e04a0d78 100644
--- a/src/extras/loaders/ColladaLoader.js
+++ b/src/extras/loaders/ColladaLoader.js
@@ -18,6 +18,7 @@ THREE.ColladaLoader = function () {
var materials = {};
var effects = {};
+ var animData;
var visualScenes;
var baseUrl;
var morphs;
@@ -26,6 +27,24 @@ THREE.ColladaLoader = function () {
var flip_uv = true;
var preferredShading = THREE.SmoothShading;
+ var options = {
+ // Axis conversion is done for geometries, animations, and controllers.
+ // If we ever pull cameras or lights out of the COLLADA file, they'll
+ // need extra work.
+ convertUpAxis: false,
+
+ subdivideFaces: true,
+
+ upAxis: 'Y'
+ };
+
+ // TODO: support unit conversion as well
+ var colladaUnit = 1.0;
+ var colladaUp = 'Y';
+ var upConversion = null;
+
+ var TO_RADIANS = Math.PI / 180;
+
function load ( url, readyCallback ) {
if ( document.implementation && document.implementation.createDocument ) {
@@ -84,6 +103,8 @@ THREE.ColladaLoader = function () {
}
+ parseAsset();
+ setUpConversion();
images = parseLib( "//dae:library_images/dae:image", _Image, "image" );
materials = parseLib( "//dae:library_materials/dae:material", Material, "material") ;
effects = parseLib( "//dae:library_effects/dae:effect", Effect, "effect" );
@@ -111,6 +132,7 @@ THREE.ColladaLoader = function () {
scene: scene,
morphs: morphs,
skins: skins,
+ animations: animData,
dae: {
images: images,
materials: materials,
@@ -140,6 +162,49 @@ THREE.ColladaLoader = function () {
};
+ function parseAsset () {
+
+ var elements = COLLADA.evaluate("//dae:asset",
+ COLLADA,
+ _nsResolver,
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE,
+ null) ;
+
+ var element = elements.iterateNext();
+
+ if ( element ) {
+
+ for ( var i = 0; i < element.children.length; i ++ ) {
+
+ var child = element.children[ i ];
+
+ switch ( child.nodeName ) {
+
+ case 'unit':
+
+ var meter = child.getAttribute( 'meter' );
+
+ if ( meter ) {
+
+ colladaUnit = parseFloat( meter );
+
+ }
+
+ break;
+
+ case 'up_axis':
+
+ colladaUp = child.textContent.charAt(0);
+ break;
+
+ }
+
+ }
+
+ }
+
+ };
+
function parseLib ( q, classSpec, prefix ) {
var elements = COLLADA.evaluate(q,
@@ -188,17 +253,67 @@ THREE.ColladaLoader = function () {
function createAnimations () {
- calcAnimationBounds();
+ animData = [];
- for ( var animation_id in animations ) {
-
- createAnimation( animations[ animation_id ] );
-
- }
+ // fill in the keys
+ recurseHierarchy( scene );
};
- function createAnimation ( animation ) {
+ function recurseHierarchy ( node ) {
+
+ var n = daeScene.getChildById( node.name, true ),
+ newData = null;
+
+ if ( n && n.keys ) {
+
+ newData = {
+ fps: 60,
+ hierarchy: [ {
+ node: n,
+ keys: n.keys,
+ sids: n.sids
+ } ],
+ node: node,
+ name: 'animation_' + node.name,
+ length: 0
+ };
+
+ animData.push(newData);
+
+ for ( var i = 0, il = n.keys.length; i < il; i++ ) {
+
+ newData.length = Math.max( newData.length, n.keys[i].time );
+
+ }
+
+ } else {
+
+ newData = {
+ hierarchy: [ {
+ keys: [],
+ sids: []
+ } ]
+ }
+
+ }
+
+ for ( var i = 0, il = node.children.length; i < il; i++ ) {
+
+ var d = recurseHierarchy( node.children[i] );
+
+ for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) {
+
+ newData.hierarchy.push( {
+ keys: [],
+ sids: []
+ } );
+
+ }
+
+ }
+
+ return newData;
};
@@ -853,31 +968,199 @@ THREE.ColladaLoader = function () {
if ( node.channels && node.channels.length ) {
- var frameDuration = calcFrameDuration( node );
- var t, matrix;
- var keys = [];
+ var keys = [],
+ sids = [];
- for ( t = node.startTime; t < node.endTime; t += frameDuration ) {
+ for ( var i = 0, il = node.channels.length; i < il; i++ ) {
- matrix = calcMatrixAt( node, t );
+ var channel = node.channels[i],
+ fullSid = channel.fullSid,
+ member = getConvertedMember( channel.member ),
+ sampler = channel.sampler,
+ input = sampler.input,
+ transform = node.getTransformBySid( channel.sid );
- //keys.push({time: t, mat: matrix.flatten()})
+ if ( transform ) {
- keys.push({
- time: t,
- pos: [matrix.n14, matrix.n24, matrix.n34],
- rotq: [0, 0, 0, 1],
- scl: [1,1,1]
- });
+ if ( sids.indexOf( fullSid ) === -1 ) {
+
+ sids.push( fullSid );
+
+ }
+
+ for ( var j = 0, jl = input.length; j < jl; j++ ) {
+
+ var time = input[j],
+ data = sampler.getData( transform.type, j ),
+ key = findKey( keys, time );
+
+ if ( !key ) {
+
+ key = new Key( time );
+ var timeNdx = findTimeNdx( keys, time );
+ keys.splice( timeNdx == -1 ? keys.length : timeNdx, 0, key );
+
+ }
+
+ key.addTarget( fullSid, transform, member, data );
+
+ }
+
+ } else {
+
+ console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id );
+
+ }
+
+ }
+
+ // post process
+ for ( var i = 0; i < sids.length; i++ ) {
+
+ var sid = sids[ i ];
+
+ for ( var j = 0; j < keys.length; j++ ) {
+
+ var key = keys[ j ];
+
+ if ( !key.hasTarget( sid ) ) {
+
+ interpolateKeys( keys, key, j, sid );
+
+ }
+
+ }
}
node.keys = keys;
+ node.sids = sids;
}
};
+ function findKey ( keys, time) {
+
+ var retVal = null;
+
+ for ( var i = 0, il = keys.length; i < il && retVal == null; i++ ) {
+
+ var key = keys[i];
+
+ if ( key.time === time ) {
+
+ retVal = key;
+
+ } else if ( key.time > time ) {
+
+ break;
+
+ }
+
+ }
+
+ return retVal;
+
+ };
+
+ function findTimeNdx ( keys, time) {
+
+ var ndx = -1;
+
+ for ( var i = 0, il = keys.length; i < il && ndx == -1; i++ ) {
+
+ var key = keys[i];
+
+ if ( key.time >= time ) {
+
+ ndx = i;
+
+ }
+
+ }
+
+ return ndx;
+
+ };
+
+ function interpolateKeys ( keys, key, ndx, fullSid ) {
+
+ var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx-1 : 0 ),
+ nextKey = getNextKeyWith( keys, fullSid, ndx+1 );
+
+ if ( prevKey && nextKey ) {
+
+ var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time),
+ prevTarget = prevKey.getTarget( fullSid ),
+ nextData = nextKey.getTarget( fullSid ).data,
+ prevData = prevTarget.data,
+ data;
+
+ if ( prevData.length ) {
+
+ data = [];
+
+ for ( var i = 0; i < prevData.length; ++i ) {
+
+ data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale;
+
+ }
+
+ } else {
+
+ data = prevData + ( nextData - prevData ) * scale;
+
+ }
+
+ key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data );
+
+ }
+
+ };
+
+ // Get next key with given sid
+
+ function getNextKeyWith( keys, fullSid, ndx ) {
+
+ for ( ; ndx < keys.length; ndx++ ) {
+
+ var key = keys[ ndx ];
+
+ if ( key.hasTarget( fullSid ) ) {
+
+ return key;
+
+ }
+
+ }
+
+ return null;
+
+ };
+
+ // Get previous key with given sid
+
+ function getPrevKeyWith( keys, fullSid, ndx ) {
+
+ ndx = ndx >= 0 ? ndx : ndx + keys.length;
+
+ for ( ; ndx >= 0; ndx-- ) {
+
+ var key = keys[ ndx ];
+
+ if ( key.hasTarget( fullSid ) ) {
+
+ return key;
+
+ }
+
+ }
+
+ return null;
+
+ };
+
function _Image() {
this.id = "";
@@ -1076,13 +1359,7 @@ THREE.ColladaLoader = function () {
case 'bind_shape_matrix':
var f = _floats(child.textContent);
- this.bindShapeMatrix = new THREE.Matrix4();
- this.bindShapeMatrix.set(
- f[0], f[1], f[2], f[3],
- f[4], f[5], f[6], f[7],
- f[8], f[9], f[10], f[11],
- f[12], f[13], f[14], f[15]
- );
+ this.bindShapeMatrix = getConvertedMat4( f );
break;
case 'source':
@@ -1531,7 +1808,7 @@ THREE.ColladaLoader = function () {
for ( var i = 0; i < this.transforms.length; i ++ ) {
- this.matrix.multiply( this.matrix, this.transforms[ i ].matrix );
+ this.transforms[ i ].apply( this.matrix );
}
@@ -1542,7 +1819,7 @@ THREE.ColladaLoader = function () {
this.sid = "";
this.type = "";
this.data = [];
- this.matrix = new THREE.Matrix4();
+ this.obj = null;
};
@@ -1551,53 +1828,149 @@ THREE.ColladaLoader = function () {
this.sid = element.getAttribute( 'sid' );
this.type = element.nodeName;
this.data = _floats( element.textContent );
-
- this.updateMatrix();
+ this.convert();
return this;
};
- Transform.prototype.updateMatrix = function () {
-
- var angle = 0;
-
- this.matrix.identity();
+ Transform.prototype.convert = function () {
switch ( this.type ) {
case 'matrix':
- this.matrix.set(
- this.data[0], this.data[1], this.data[2], this.data[3],
- this.data[4], this.data[5], this.data[6], this.data[7],
- this.data[8], this.data[9], this.data[10], this.data[11],
- this.data[12], this.data[13], this.data[14], this.data[15]
- );
- break;
-
- case 'translate':
-
- this.matrix.setTranslation(this.data[0], this.data[1], this.data[2]);
+ this.obj = getConvertedMat4( this.data );
break;
case 'rotate':
- angle = this.data[3] * (Math.PI / 180.0);
- this.matrix.setRotationAxis(new THREE.Vector3(this.data[0], this.data[1], this.data[2]), angle);
+ this.angle = this.data[3] * TO_RADIANS;
+
+ case 'translate':
+
+ fixCoords( this.data, -1 );
+ this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] );
break;
case 'scale':
- this.matrix.setScale(this.data[0], this.data[1], this.data[2]);
+ fixCoords( this.data, 1 );
+ this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] );
break;
default:
+ console.log( 'Can not convert Transform of type ' + this.type );
break;
}
- return this.matrix;
+ };
+
+ Transform.prototype.apply = function ( matrix ) {
+
+ switch ( this.type ) {
+
+ case 'matrix':
+
+ matrix.multiplySelf( this.obj );
+ break;
+
+ case 'translate':
+
+ matrix.translate( this.obj );
+ break;
+
+ case 'rotate':
+
+ matrix.rotateByAxis( this.obj, this.angle );
+ break;
+
+ case 'scale':
+
+ matrix.scale( this.obj );
+ break;
+
+ }
+
+ };
+
+ Transform.prototype.update = function ( data, member ) {
+
+ switch ( this.type ) {
+
+ case 'matrix':
+
+ console.log( 'Currently not handling matrix transform updates' );
+ break;
+
+ case 'translate':
+ case 'scale':
+
+ switch ( member ) {
+
+ case 'X':
+
+ this.obj.x = data;
+ break;
+
+ case 'Y':
+
+ this.obj.y = data;
+ break;
+
+ case 'Z':
+
+ this.obj.z = data;
+ break;
+
+ default:
+
+ this.obj.x = data[ 0 ];
+ this.obj.y = data[ 1 ];
+ this.obj.z = data[ 2 ];
+ break;
+
+ }
+
+ break;
+
+ case 'rotate':
+
+ switch ( member ) {
+
+ case 'X':
+
+ this.obj.x = data;
+ break;
+
+ case 'Y':
+
+ this.obj.y = data;
+ break;
+
+ case 'Z':
+
+ this.obj.z = data;
+ break;
+
+ case 'ANGLE':
+
+ this.angle = data * TO_RADIANS;
+ break;
+
+ default:
+
+ this.obj.x = data[ 0 ];
+ this.obj.y = data[ 1 ];
+ this.obj.z = data[ 2 ];
+ this.angle = data[ 3 ] * TO_RADIANS;
+ break;
+
+ }
+ break;
+
+ }
};
@@ -1820,7 +2193,7 @@ THREE.ColladaLoader = function () {
for ( i = 0; i < vertexData.length; i += 3 ) {
- var v = new THREE.Vertex( new THREE.Vector3( vertexData[ i ], vertexData[ i + 1 ], vertexData[ i + 2 ] ) );
+ var v = new THREE.Vertex( getConvertedVec3( vertexData, i ) );
this.geometry3js.vertices.push( v );
}
@@ -1898,7 +2271,7 @@ THREE.ColladaLoader = function () {
case 'NORMAL':
- ns.push( new THREE.Vector3( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) );
+ ns.push( getConvertedVec3( source.data, idx32 ) );
break;
@@ -1936,7 +2309,7 @@ THREE.ColladaLoader = function () {
faces.push( new THREE.Face4( vs[0], vs[1], vs[2], vs[3], [ ns[0], ns[1], ns[2], ns[3] ], cs.length ? cs : new THREE.Color() ) );
- } else if ( vcount > 4 ) {
+ } else if ( vcount > 4 && options.subdivideFaces ) {
var clr = cs.length ? cs : new THREE.Color(),
vec1, vec2, vec3, v1, v2, norm;
@@ -2248,13 +2621,7 @@ THREE.ColladaLoader = function () {
for ( var j = 0; j < this.data.length; j += 16 ) {
var s = this.data.slice( j, j + 16 );
- var m = new THREE.Matrix4();
- m.set(
- s[0], s[1], s[2], s[3],
- s[4], s[5], s[6], s[7],
- s[8], s[9], s[10], s[11],
- s[12], s[13], s[14], s[15]
- );
+ var m = getConvertedMat4( s );
result.push( m );
}
@@ -2911,6 +3278,7 @@ THREE.ColladaLoader = function () {
this.animation = animation;
this.source = "";
this.target = "";
+ this.fullSid = null;
this.sid = null;
this.dotSyntax = null;
this.arrSyntax = null;
@@ -2932,19 +3300,16 @@ THREE.ColladaLoader = function () {
var dotSyntax = ( sid.indexOf(".") >= 0 );
var arrSyntax = ( sid.indexOf("(") >= 0 );
- var arrIndices;
- var member;
-
if ( dotSyntax ) {
parts = sid.split(".");
- sid = parts.shift();
- member = parts.shift();
+ this.sid = parts.shift();
+ this.member = parts.shift();
} else if ( arrSyntax ) {
- arrIndices = sid.split("(");
- sid = arrIndices.shift();
+ var arrIndices = sid.split("(");
+ this.sid = arrIndices.shift();
for (var j = 0; j < arrIndices.length; j ++ ) {
@@ -2952,13 +3317,17 @@ THREE.ColladaLoader = function () {
}
+ this.arrIndices = arrIndices;
+
+ } else {
+
+ this.sid = sid;
+
}
- this.sid = sid;
+ this.fullSid = sid;
this.dotSyntax = dotSyntax;
this.arrSyntax = arrSyntax;
- this.arrIndices = arrIndices;
- this.member = member;
return this;
@@ -2971,6 +3340,7 @@ THREE.ColladaLoader = function () {
this.inputs = [];
this.input = null;
this.output = null;
+ this.strideOut = null;
this.interpolation = null;
this.startTime = null;
this.endTime = null;
@@ -3023,6 +3393,7 @@ THREE.ColladaLoader = function () {
case 'OUTPUT':
this.output = source.read();
+ this.strideOut = source.accessor.stride;
break;
case 'INTERPOLATION':
@@ -3069,6 +3440,168 @@ THREE.ColladaLoader = function () {
};
+ Sampler.prototype.getData = function ( type, ndx ) {
+
+ var data;
+
+ if ( this.strideOut > 1 ) {
+
+ data = [];
+ ndx *= this.strideOut;
+
+ for ( var i = 0; i < this.strideOut; ++i ) {
+
+ data[ i ] = this.output[ ndx + i ];
+
+ }
+
+ if ( this.strideOut === 3 ) {
+
+ switch ( type ) {
+
+ case 'rotate':
+ case 'translate':
+
+ fixCoords( data, -1 );
+ break;
+
+ case 'scale':
+
+ fixCoords( data, 1 );
+ break;
+
+ }
+
+ }
+
+ } else {
+
+ data = this.output[ ndx ];
+
+ }
+
+ return data;
+
+ };
+
+ function Key ( time ) {
+
+ this.targets = [];
+ this.time = time;
+
+ };
+
+ Key.prototype.addTarget = function ( fullSid, transform, member, data ) {
+
+ this.targets.push( {
+ sid: fullSid,
+ member: member,
+ transform: transform,
+ data: data
+ } );
+
+ };
+
+ Key.prototype.apply = function ( opt_sid ) {
+
+ for ( var i = 0; i < this.targets.length; ++i ) {
+
+ var target = this.targets[ i ];
+
+ if ( !opt_sid || target.sid === opt_sid ) {
+
+ target.transform.update( target.data, target.member );
+
+ }
+
+ }
+
+ };
+
+ Key.prototype.getTarget = function ( fullSid ) {
+
+ for ( var i = 0; i < this.targets.length; ++i ) {
+
+ if ( this.targets[ i ].sid === fullSid ) {
+
+ return this.targets[ i ];
+
+ }
+
+ }
+
+ return null;
+
+ };
+
+ Key.prototype.hasTarget = function ( fullSid ) {
+
+ for ( var i = 0; i < this.targets.length; ++i ) {
+
+ if ( this.targets[ i ].sid === fullSid ) {
+
+ return true;
+
+ }
+
+ }
+
+ return false;
+
+ };
+
+ // TODO: Currently only doing linear interpolation. Should support full COLLADA spec.
+ Key.prototype.interpolate = function ( nextKey, time ) {
+
+ for ( var i = 0; i < this.targets.length; ++i ) {
+
+ var target = this.targets[ i ],
+ nextTarget = nextKey.getTarget( target.sid ),
+ data;
+
+ if ( nextTarget ) {
+
+ var scale = ( time - this.time ) / ( nextKey.time - this.time ),
+ nextData = nextTarget.data,
+ prevData = target.data;
+
+ // check scale error
+
+ if ( scale < 0 || scale > 1 ) {
+
+ console.log( "Key.interpolate: Warning! Scale out of bounds:" + scale );
+ scale = scale < 0 ? 0 : 1;
+
+ }
+
+ if ( prevData.length ) {
+
+ data = [];
+
+ for ( var j = 0; j < prevData.length; ++j ) {
+
+ data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale;
+
+ }
+
+ } else {
+
+ data = prevData + ( nextData - prevData ) * scale;
+
+ }
+
+ } else {
+
+ data = target.data;
+
+ }
+
+ target.transform.update( data, target.member );
+
+ }
+
+ };
+
function _source ( element ) {
var id = element.getAttribute( 'id' );
@@ -3248,13 +3781,246 @@ THREE.ColladaLoader = function () {
};
+ // Up axis conversion
+
+ function setUpConversion () {
+
+ if ( !options.convertUpAxis || colladaUp === options.upAxis ) {
+
+ upConversion = null;
+
+ } else {
+
+ switch ( colladaUp ) {
+
+ case 'X':
+
+ upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ';
+ break;
+
+ case 'Y':
+
+ upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ';
+ break;
+
+ case 'Z':
+
+ upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY';
+ break;
+
+ }
+
+ }
+
+ };
+
+ function fixCoords ( data, sign ) {
+
+ if ( !options.convertUpAxis || colladaUp === options.upAxis ) {
+
+ return;
+
+ }
+
+ switch ( upConversion ) {
+
+ case 'XtoY':
+
+ var tmp = data[ 0 ];
+ data[ 0 ] = sign * data[ 1 ];
+ data[ 1 ] = tmp;
+ break;
+
+ case 'XtoZ':
+
+ var tmp = data[ 2 ];
+ data[ 2 ] = data[ 1 ];
+ data[ 1 ] = data[ 0 ];
+ data[ 0 ] = tmp;
+ break;
+
+ case 'YtoX':
+
+ var tmp = data[ 0 ];
+ data[ 0 ] = data[ 1 ];
+ data[ 1 ] = sign * tmp;
+ break;
+
+ case 'YtoZ':
+
+ var tmp = data[ 1 ];
+ data[ 1 ] = sign * data[ 2 ];
+ data[ 2 ] = tmp;
+ break;
+
+ case 'ZtoX':
+
+ var tmp = data[ 0 ];
+ data[ 0 ] = data[ 1 ];
+ data[ 1 ] = data[ 2 ];
+ data[ 2 ] = tmp;
+ break;
+
+ case 'ZtoY':
+
+ var tmp = data[ 1 ];
+ data[ 1 ] = data[ 2 ];
+ data[ 2 ] = sign * tmp;
+ break;
+
+ }
+
+ };
+
+ function getConvertedVec3 ( data, offset ) {
+
+ var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ];
+ fixCoords( arr, -1 );
+ return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] );
+
+ };
+
+ function getConvertedMat4 ( data ) {
+
+ if ( options.convertUpAxis ) {
+
+ // First fix rotation and scale
+
+ // Columns first
+ var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ];
+ fixCoords( arr, -1 );
+ data[ 0 ] = arr[ 0 ];
+ data[ 4 ] = arr[ 1 ];
+ data[ 8 ] = arr[ 2 ];
+ arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ];
+ fixCoords( arr, -1 );
+ data[ 1 ] = arr[ 0 ];
+ data[ 5 ] = arr[ 1 ];
+ data[ 9 ] = arr[ 2 ];
+ arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ];
+ fixCoords( arr, -1 );
+ data[ 2 ] = arr[ 0 ];
+ data[ 6 ] = arr[ 1 ];
+ data[ 10 ] = arr[ 2 ];
+ // Rows second
+ arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ];
+ fixCoords( arr, -1 );
+ data[ 0 ] = arr[ 0 ];
+ data[ 1 ] = arr[ 1 ];
+ data[ 2 ] = arr[ 2 ];
+ arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ];
+ fixCoords( arr, -1 );
+ data[ 4 ] = arr[ 0 ];
+ data[ 5 ] = arr[ 1 ];
+ data[ 6 ] = arr[ 2 ];
+ arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ];
+ fixCoords( arr, -1 );
+ data[ 8 ] = arr[ 0 ];
+ data[ 9 ] = arr[ 1 ];
+ data[ 10 ] = arr[ 2 ];
+
+ // Now fix translation
+ arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ];
+ fixCoords( arr, -1 );
+ data[ 3 ] = arr[ 0 ];
+ data[ 7 ] = arr[ 1 ];
+ data[ 11 ] = arr[ 2 ];
+
+ }
+
+ return new THREE.Matrix4(
+ data[0], data[1], data[2], data[3],
+ data[4], data[5], data[6], data[7],
+ data[8], data[9], data[10], data[11],
+ data[12], data[13], data[14], data[15]
+ );
+
+ };
+
+ function getConvertedMember ( member ) {
+
+ if ( options.convertUpAxis ) {
+
+ switch ( member ) {
+
+ case 'X':
+
+ switch ( upConversion ) {
+
+ case 'XtoY':
+ case 'XtoZ':
+ case 'YtoX':
+
+ member = 'Y';
+ break;
+
+ case 'ZtoX':
+
+ member = 'Z';
+ break;
+
+ }
+
+ break;
+
+ case 'Y':
+
+ switch ( upConversion ) {
+
+ case 'XtoY':
+ case 'YtoX':
+ case 'ZtoX':
+
+ member = 'X';
+ break;
+
+ case 'XtoZ':
+ case 'YtoZ':
+ case 'ZtoY':
+
+ member = 'Z';
+ break;
+
+ }
+
+ break;
+
+ case 'Z':
+
+ switch ( upConversion ) {
+
+ case 'XtoZ':
+
+ member = 'X';
+ break;
+
+ case 'YtoZ':
+ case 'ZtoX':
+ case 'ZtoY':
+
+ member = 'Y';
+ break;
+
+ }
+
+ break;
+
+ }
+
+ }
+
+ return member;
+
+ };
+
return {
load: load,
parse: parse,
setPreferredShading: setPreferredShading,
applySkin: applySkin,
- geometries : geometries
+ geometries : geometries,
+ options: options
};