|
@@ -7,7 +7,8 @@ THREE.ColladaLoader = function () {
|
|
|
|
|
|
var COLLADA = null;
|
|
|
var scene = null;
|
|
|
- var daeScene;
|
|
|
+ var visualScene;
|
|
|
+ var kinematicsModel;
|
|
|
|
|
|
var readyCallbackFunc = null;
|
|
|
|
|
@@ -22,7 +23,9 @@ THREE.ColladaLoader = function () {
|
|
|
var lights = {};
|
|
|
|
|
|
var animData;
|
|
|
+ var kinematics;
|
|
|
var visualScenes;
|
|
|
+ var kinematicsModel;
|
|
|
var baseUrl;
|
|
|
var morphs;
|
|
|
var skins;
|
|
@@ -141,16 +144,17 @@ THREE.ColladaLoader = function () {
|
|
|
controllers = parseLib( "library_controllers controller", Controller, "controller" );
|
|
|
animations = parseLib( "library_animations animation", Animation, "animation" );
|
|
|
visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" );
|
|
|
+ kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" );
|
|
|
|
|
|
morphs = [];
|
|
|
skins = [];
|
|
|
|
|
|
- daeScene = parseScene();
|
|
|
+ visualScene = parseScene();
|
|
|
scene = new THREE.Scene();
|
|
|
|
|
|
- for ( var i = 0; i < daeScene.nodes.length; i ++ ) {
|
|
|
+ for ( var i = 0; i < visualScene.nodes.length; i ++ ) {
|
|
|
|
|
|
- scene.add( createSceneGraph( daeScene.nodes[ i ] ) );
|
|
|
+ scene.add( createSceneGraph( visualScene.nodes[ i ] ) );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -159,12 +163,16 @@ THREE.ColladaLoader = function () {
|
|
|
|
|
|
createAnimations();
|
|
|
|
|
|
+ kinematicsModel = parseKinematicsModel();
|
|
|
+ createKinematics();
|
|
|
+
|
|
|
var result = {
|
|
|
|
|
|
scene: scene,
|
|
|
morphs: morphs,
|
|
|
skins: skins,
|
|
|
animations: animData,
|
|
|
+ kinematics: kinematics,
|
|
|
dae: {
|
|
|
images: images,
|
|
|
materials: materials,
|
|
@@ -175,7 +183,10 @@ THREE.ColladaLoader = function () {
|
|
|
controllers: controllers,
|
|
|
animations: animations,
|
|
|
visualScenes: visualScenes,
|
|
|
- scene: daeScene
|
|
|
+ visualScene: visualScene,
|
|
|
+ scene: visualScene,
|
|
|
+ kinematicsModels: kinematicsModels,
|
|
|
+ kinematicsModel: kinematicsModel
|
|
|
}
|
|
|
|
|
|
};
|
|
@@ -276,6 +287,23 @@ THREE.ColladaLoader = function () {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ function parseKinematicsModel() {
|
|
|
+
|
|
|
+ var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0];
|
|
|
+
|
|
|
+ if ( kinematicsModelElement ) {
|
|
|
+
|
|
|
+ var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, '');
|
|
|
+ return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ];
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ return null;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
function createAnimations() {
|
|
|
|
|
|
animData = [];
|
|
@@ -287,7 +315,7 @@ THREE.ColladaLoader = function () {
|
|
|
|
|
|
function recurseHierarchy( node ) {
|
|
|
|
|
|
- var n = daeScene.getChildById( node.id, true ),
|
|
|
+ var n = visualScene.getChildById( node.id, true ),
|
|
|
newData = null;
|
|
|
|
|
|
if ( n && n.keys ) {
|
|
@@ -427,7 +455,7 @@ THREE.ColladaLoader = function () {
|
|
|
}
|
|
|
|
|
|
var skin = skinCtrl.skin;
|
|
|
- var skeleton = daeScene.getChildById( ctrl.skeleton[ 0 ] );
|
|
|
+ var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] );
|
|
|
var hierarchy = [];
|
|
|
|
|
|
applyBindShape = applyBindShape !== undefined ? applyBindShape : true;
|
|
@@ -663,8 +691,8 @@ THREE.ColladaLoader = function () {
|
|
|
}
|
|
|
|
|
|
var animationBounds = calcAnimationBounds();
|
|
|
- var skeleton = daeScene.getChildById( instanceCtrl.skeleton[0], true ) ||
|
|
|
- daeScene.getChildBySid( instanceCtrl.skeleton[0], true );
|
|
|
+ var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) ||
|
|
|
+ visualScene.getChildBySid( instanceCtrl.skeleton[0], true );
|
|
|
|
|
|
//flatten the skeleton into a list of bones
|
|
|
var bonelist = flattenSkeleton(skeleton);
|
|
@@ -793,6 +821,210 @@ THREE.ColladaLoader = function () {
|
|
|
|
|
|
};
|
|
|
|
|
|
+ function createKinematics() {
|
|
|
+
|
|
|
+ if ( kinematicsModel && kinematicsModel.joints.length === 0 ) {
|
|
|
+ kinematics = undefined;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var jointMap = {};
|
|
|
+
|
|
|
+ var _addToMap = function( jointIndex, parentVisualElement ) {
|
|
|
+
|
|
|
+ var parentVisualElementId = parentVisualElement.getAttribute( 'id' );
|
|
|
+ var colladaNode = visualScene.getChildById( parentVisualElementId, true );
|
|
|
+ var joint = kinematicsModel.joints[ jointIndex ];
|
|
|
+
|
|
|
+ scene.traverse(function( node ) {
|
|
|
+
|
|
|
+ if ( node.id == parentVisualElementId ) {
|
|
|
+
|
|
|
+ jointMap[ jointIndex ] = {
|
|
|
+ node: node,
|
|
|
+ transforms: colladaNode.transforms,
|
|
|
+ joint: joint,
|
|
|
+ position: joint.zeroPosition
|
|
|
+ };
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ });
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ kinematics = {
|
|
|
+
|
|
|
+ joints: kinematicsModel && kinematicsModel.joints,
|
|
|
+
|
|
|
+ getJointValue: function( jointIndex ) {
|
|
|
+
|
|
|
+ var jointData = jointMap[ jointIndex ];
|
|
|
+
|
|
|
+ if ( jointData ) {
|
|
|
+
|
|
|
+ return jointData.position;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ setJointValue: function( jointIndex, value ) {
|
|
|
+
|
|
|
+ var jointData = jointMap[ jointIndex ];
|
|
|
+
|
|
|
+ if ( jointData ) {
|
|
|
+
|
|
|
+ var joint = jointData.joint;
|
|
|
+
|
|
|
+ if ( value > joint.limits.max || value < joint.limits.min ) {
|
|
|
+
|
|
|
+ console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' );
|
|
|
+
|
|
|
+ } else if ( joint.static ) {
|
|
|
+
|
|
|
+ console.log( 'setJointValue: joint ' + jointIndex + ' is static' );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ var threejsNode = jointData.node;
|
|
|
+ var axis = joint.axis;
|
|
|
+ var transforms = jointData.transforms;
|
|
|
+
|
|
|
+ var matrix = new THREE.Matrix4();
|
|
|
+
|
|
|
+ for (i = 0; i < transforms.length; i++ ) {
|
|
|
+
|
|
|
+ var transform = transforms[ i ];
|
|
|
+
|
|
|
+ // kinda ghetto joint detection
|
|
|
+ if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) {
|
|
|
+
|
|
|
+ // apply actual joint value here
|
|
|
+ switch ( joint.type ) {
|
|
|
+
|
|
|
+ case 'revolute':
|
|
|
+
|
|
|
+ matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'prismatic':
|
|
|
+
|
|
|
+ matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) );
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+
|
|
|
+ console.warn( 'setJointValue: unknown joint type: ' + joint.type );
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ var m1 = new THREE.Matrix4();
|
|
|
+
|
|
|
+ switch ( transform.type ) {
|
|
|
+
|
|
|
+ case 'matrix':
|
|
|
+
|
|
|
+ matrix.multiply( transform.obj );
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'translate':
|
|
|
+
|
|
|
+ matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) );
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'rotate':
|
|
|
+
|
|
|
+ matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) );
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // apply the matrix to the threejs node
|
|
|
+ var elementsFloat32Arr = matrix.elements;
|
|
|
+ var elements = Array.prototype.slice.call( elementsFloat32Arr );
|
|
|
+
|
|
|
+ var elementsRowMajor = [
|
|
|
+ elements[ 0 ],
|
|
|
+ elements[ 4 ],
|
|
|
+ elements[ 8 ],
|
|
|
+ elements[ 12 ],
|
|
|
+ elements[ 1 ],
|
|
|
+ elements[ 5 ],
|
|
|
+ elements[ 9 ],
|
|
|
+ elements[ 13 ],
|
|
|
+ elements[ 2 ],
|
|
|
+ elements[ 6 ],
|
|
|
+ elements[ 10 ],
|
|
|
+ elements[ 14 ],
|
|
|
+ elements[ 3 ],
|
|
|
+ elements[ 7 ],
|
|
|
+ elements[ 11 ],
|
|
|
+ elements[ 15 ]
|
|
|
+ ];
|
|
|
+
|
|
|
+ threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor );
|
|
|
+ threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+
|
|
|
+ console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ var element = COLLADA.querySelector('scene instance_kinematics_scene');
|
|
|
+
|
|
|
+ if ( element ) {
|
|
|
+
|
|
|
+ for (var i = 0; i < element.childNodes.length; i++ ) {
|
|
|
+
|
|
|
+ var child = element.childNodes[ i ];
|
|
|
+ if ( child.nodeType != 1 ) continue;
|
|
|
+
|
|
|
+ switch ( child.nodeName ) {
|
|
|
+
|
|
|
+ case 'bind_joint_axis':
|
|
|
+
|
|
|
+ var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop();
|
|
|
+ var axis = child.querySelector('axis param').textContent;
|
|
|
+ var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] );
|
|
|
+ var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' );
|
|
|
+
|
|
|
+ if ( visualTargetElement ) {
|
|
|
+ var parentVisualElement = visualTargetElement.parentElement;
|
|
|
+ _addToMap(jointIndex, parentVisualElement);
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
function createSceneGraph ( node, parent ) {
|
|
|
|
|
|
var obj = new THREE.Object3D();
|
|
@@ -4539,6 +4771,231 @@ THREE.ColladaLoader = function () {
|
|
|
|
|
|
};
|
|
|
|
|
|
+ function KinematicsModel( ) {
|
|
|
+
|
|
|
+ this.id = '';
|
|
|
+ this.name = '';
|
|
|
+ this.joints = [];
|
|
|
+ this.links = [];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ KinematicsModel.prototype.parse = function( element ) {
|
|
|
+
|
|
|
+ this.id = element.getAttribute('id');
|
|
|
+ this.name = element.getAttribute('name');
|
|
|
+ this.joints = [];
|
|
|
+ this.links = [];
|
|
|
+
|
|
|
+ for (var i = 0; i < element.childNodes.length; i++ ) {
|
|
|
+
|
|
|
+ var child = element.childNodes[ i ];
|
|
|
+ if ( child.nodeType != 1 ) continue;
|
|
|
+
|
|
|
+ switch ( child.nodeName ) {
|
|
|
+
|
|
|
+ case 'technique_common':
|
|
|
+
|
|
|
+ this.parseCommon(child);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return this;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ KinematicsModel.prototype.parseCommon = function( element ) {
|
|
|
+
|
|
|
+ for (var i = 0; i < element.childNodes.length; i++ ) {
|
|
|
+
|
|
|
+ var child = element.childNodes[ i ];
|
|
|
+ if ( child.nodeType != 1 ) continue;
|
|
|
+
|
|
|
+ switch ( element.childNodes[ i ].nodeName ) {
|
|
|
+
|
|
|
+ case 'joint':
|
|
|
+ this.joints.push( (new Joint()).parse(child) );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'link':
|
|
|
+ this.links.push( (new Link()).parse(child) );
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return this;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ function Joint( ) {
|
|
|
+
|
|
|
+ this.sid = '';
|
|
|
+ this.name = '';
|
|
|
+ this.axis = new THREE.Vector3();
|
|
|
+ this.limits = {
|
|
|
+ min: 0,
|
|
|
+ max: 0
|
|
|
+ };
|
|
|
+ this.type = '';
|
|
|
+ this.static = false;
|
|
|
+ this.zeroPosition = 0.0;
|
|
|
+ this.middlePosition = 0.0;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ Joint.prototype.parse = function( element ) {
|
|
|
+
|
|
|
+ this.sid = element.getAttribute('sid');
|
|
|
+ this.name = element.getAttribute('name');
|
|
|
+ this.axis = new THREE.Vector3();
|
|
|
+ this.limits = {
|
|
|
+ min: 0,
|
|
|
+ max: 0
|
|
|
+ };
|
|
|
+ this.type = '';
|
|
|
+ this.static = false;
|
|
|
+ this.zeroPosition = 0.0;
|
|
|
+ this.middlePosition = 0.0;
|
|
|
+
|
|
|
+ var axisElement = element.querySelector('axis');
|
|
|
+ var _axis = _floats(axisElement.textContent);
|
|
|
+ this.axis = getConvertedVec3(_axis, 0);
|
|
|
+
|
|
|
+ var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360;
|
|
|
+ var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360;
|
|
|
+
|
|
|
+ this.limits = {
|
|
|
+ min: min,
|
|
|
+ max: max
|
|
|
+ };
|
|
|
+
|
|
|
+ var jointTypes = ['prismatic', 'revolute'];
|
|
|
+ for (var i = 0; i < jointTypes.length; i++ ) {
|
|
|
+
|
|
|
+ var type = jointTypes[ i ];
|
|
|
+
|
|
|
+ var jointElement = element.querySelector(type);
|
|
|
+
|
|
|
+ if ( jointElement ) {
|
|
|
+
|
|
|
+ this.type = type;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // if the min is equal to or somehow greater than the max, consider the joint static
|
|
|
+ if ( this.limits.min >= this.limits.max ) {
|
|
|
+
|
|
|
+ this.static = true;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this.middlePosition = (this.limits.min + this.limits.max) / 2.0;
|
|
|
+ return this;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ function Link( ) {
|
|
|
+
|
|
|
+ this.sid = '';
|
|
|
+ this.name = '';
|
|
|
+ this.transforms = [];
|
|
|
+ this.attachments = [];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ Link.prototype.parse = function( element ) {
|
|
|
+
|
|
|
+ this.sid = element.getAttribute('sid');
|
|
|
+ this.name = element.getAttribute('name');
|
|
|
+ this.transforms = [];
|
|
|
+ this.attachments = [];
|
|
|
+
|
|
|
+ for (var i = 0; i < element.childNodes.length; i++ ) {
|
|
|
+
|
|
|
+ var child = element.childNodes[ i ];
|
|
|
+ if ( child.nodeType != 1 ) continue;
|
|
|
+
|
|
|
+ switch ( child.nodeName ) {
|
|
|
+
|
|
|
+ case 'attachment_full':
|
|
|
+ this.attachments.push( (new Attachment()).parse(child) );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'rotate':
|
|
|
+ case 'translate':
|
|
|
+ case 'matrix':
|
|
|
+
|
|
|
+ this.transforms.push( (new Transform()).parse(child) );
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return this;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ function Attachment( ) {
|
|
|
+
|
|
|
+ this.joint = '';
|
|
|
+ this.transforms = [];
|
|
|
+ this.links = [];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ Attachment.prototype.parse = function( element ) {
|
|
|
+
|
|
|
+ this.joint = element.getAttribute('joint').split('/').pop();
|
|
|
+ this.links = [];
|
|
|
+
|
|
|
+ for (var i = 0; i < element.childNodes.length; i++ ) {
|
|
|
+
|
|
|
+ var child = element.childNodes[ i ];
|
|
|
+ if ( child.nodeType != 1 ) continue;
|
|
|
+
|
|
|
+ switch ( child.nodeName ) {
|
|
|
+
|
|
|
+ case 'link':
|
|
|
+ this.links.push( (new Link()).parse(child) );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'rotate':
|
|
|
+ case 'translate':
|
|
|
+ case 'matrix':
|
|
|
+
|
|
|
+ this.transforms.push( (new Transform()).parse(child) );
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return this;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
function _source( element ) {
|
|
|
|
|
|
var id = element.getAttribute( 'id' );
|