elation-engine
Version:
WebGL/WebVR engine written in Javascript
350 lines (327 loc) • 13.7 kB
JavaScript
elation.require(['engine.external.leapmotion.leap'], function() {
elation.component.add('engine.things.leapmotion', function() {
this.postinit = function() {
Leap.loop(elation.bind(this, this.loop));
}
this.createChildren = function() {
this.hands = {
left: this.spawn('leapmotion_hand', 'hand_left', { position: new THREE.Vector3(-.1, 0, 0) }),
right: this.spawn('leapmotion_hand', 'hand_right', { position: new THREE.Vector3(.1, 0, 0) })
};
}
this.loop = function(frame) {
for (var i = 0; i < frame.hands.length; i++) {
var hand = frame.hands[i];
var handobj = this.hands[hand.type];
if (handobj) {
handobj.updateData(hand, 1);
}
}
}
}, elation.engine.things.generic);
elation.component.add('engine.things.leapmotion_hand', function() {
this.postinit = function() {
this.palmrotation = new THREE.Euler();
this.palmPosition = new THREE.Vector3();
this.palmOrientation = new THREE.Quaternion();
this.fingerTips = [
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3()
];
}
this.createObject3D = function() {
var hand = new THREE.Object3D();
return hand;
}
this.createChildren = function() {
//var palmsize = [.07,.01,.05];
var palmsize = [1,.01,1];
this.materials = {
bones: new THREE.MeshStandardMaterial({color: 0xdddddd, transparent: false, opacity: 1, blending: THREE.NormalBlending, side: THREE.DoubleSide, envMap: this.engine.systems.world.scene["world-3d"].background, roughness: .75}),
joints: new THREE.MeshPhongMaterial({color: 0xccffcc, transparent: false, opacity: 1, blending: THREE.NormalBlending}),
tips: new THREE.MeshPhongMaterial({color: 0xffcccc, transparent: true, opacity: .2, blending: THREE.NormalBlending})
};
this.palm = new THREE.Mesh(new THREE.BoxGeometry(palmsize[0], palmsize[1], palmsize[2]), this.materials.bones);
this.palm.geometry.applyMatrix(new THREE.Matrix4().setPosition(new THREE.Vector3(0, 0, -palmsize[2] / 4)));
this.palm.scale.set(.01,.01,.01);
this.palm.updateMatrix();
this.palm.matrixAutoUpdate = false;
this.palm.castShadow = true;
this.palm.receiveShadow = true;
this.objects['3d'].add(this.palm);
this.arm = new THREE.Mesh(new THREE.CylinderGeometry(0.75, 0.5, 1, undefined, undefined, true), this.materials.bones);
//this.arm.geometry.applyMatrix(new THREE.Matrix4().setPosition(new THREE.Vector3(0, 0, -palmsize[2] / 4)));
this.arm.geometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI/2));
this.arm.scale.set(.01,.01,.01);
this.arm.updateMatrix();
this.arm.matrixAutoUpdate = false;
this.arm.castShadow = true;
this.arm.receiveShadow = true;
this.objects['3d'].add(this.arm);
this.wrist = new THREE.Mesh(new THREE.SphereGeometry(0.5), this.materials.joints);
this.wrist.scale.set(.01,.01,.01);
this.wrist.updateMatrix();
//this.wrist.matrixAutoUpdate = false;
this.objects['3d'].add(this.wrist);
this.fingers = [];
for (var i = 0; i < 5; i++) {
this.fingers[i] = this.spawn('leapmotion_finger', this.name + '_finger_' + i, { hand: this });
}
}
this.updateData = (function() {
var pos = new THREE.Vector3(),
xdir = new THREE.Vector3(),
ydir = new THREE.Vector3(),
zdir = new THREE.Vector3(),
fingerbase1 = new THREE.Vector3(),
fingerbase2 = new THREE.Vector3(),
fingerbase3 = new THREE.Vector3();
return function(data, scalefactor) {
if (!scalefactor) scalefactor = 1;
if (!this.palm) return;
for (var i = 0; i < data.fingers.length; i++) {
this.fingers[i].updateData(data.fingers[i], scalefactor);
if (data.fingers[i].tipPosition) {
this.fingerTips[i].fromArray(data.fingers[i].tipPosition).multiplyScalar(scalefactor);
}
}
pos.fromArray(data.palmPosition).multiplyScalar(scalefactor);
zdir.fromArray(data.direction);
ydir.fromArray(data.palmNormal);
xdir.crossVectors(ydir, zdir);
fingerbase1.setFromMatrixPosition(this.fingers[1].phalanges[0].matrixWorld);
fingerbase2.setFromMatrixPosition(this.fingers[4].phalanges[0].matrixWorld);
fingerbase3.setFromMatrixPosition(this.fingers[1].phalanges[1].matrixWorld);
var scale = [
fingerbase1.distanceTo(fingerbase2) * 1.4,
1,
fingerbase1.distanceTo(fingerbase3) * 1.4
];
this.palm.matrix.set(
xdir.x * scale[0], ydir.x * scale[1], zdir.x * scale[2], pos.x,
xdir.y * scale[0], ydir.y * scale[1], zdir.y * scale[2], pos.y,
xdir.z * scale[0], ydir.z * scale[1], zdir.z * scale[2], pos.z,
0, 0, 0, 1
);
this.palm.updateMatrixWorld();
this.palmPosition.setFromMatrixPosition(this.palm.matrixWorld);
this.palmOrientation.setFromRotationMatrix(this.palm.matrixWorld);
if (data.arm) {
pos.fromArray(data.arm.center()).multiplyScalar(scalefactor);
xdir.fromArray(data.arm.basis[0]);
ydir.fromArray(data.arm.basis[1]);
zdir.fromArray(data.arm.basis[2]);
scale = [
data.arm.width * scalefactor,
data.arm.width * scalefactor * .75,
data.arm.length * scalefactor
];
this.arm.matrix.set(
xdir.x * scale[0], ydir.x * scale[1], zdir.x * scale[2], pos.x,
xdir.y * scale[0], ydir.y * scale[1], zdir.y * scale[2], pos.y,
xdir.z * scale[0], ydir.z * scale[1], zdir.z * scale[2], pos.z,
0, 0, 0, 1
);
this.arm.updateMatrixWorld();
this.wrist.position.fromArray(data.arm.nextJoint).multiplyScalar(scalefactor);
this.wrist.rotation.setFromRotationMatrix(this.arm.matrix);
this.wrist.scale.set(scale[0], scale[1], scale[1]/4);
}
if (!this.materials.bones.envMap) {
this.materials.bones.needsUpdate = true;
}
var worldbg = this.engine.systems.world.scene["world-3d"].background;
this.materials.bones.envMap = worldbg;
this.refresh();
};
})();
this.setState = function(state, transform) {
var values = state.split(' ').map(parseFloat);
//console.log(values);
var pos = new THREE.Vector3();
var data = {
palmPosition: pos.fromArray([values[12], values[13], values[14]]).applyMatrix4(transform).toArray(),
direction: [values[8], values[9], values[10]],
palmNormal: [values[4], values[5], values[6]],
fingers: []
};
for (var i = 0; i < 5; i++) {
data.fingers[i] = {
bones: [],
positions: [[0,0,0]]
};
for (var j = 0; j < 4; j++) {
var offset = (i * 4 + j + 1) * 16;
var center = pos.fromArray(values.slice(offset + 12, offset + 15)).toArray();
var bone = {
basis: [
// Change order to match native: Y, Z, X
values.slice(offset + 4, offset + 7),
values.slice(offset + 8, offset + 11),
values.slice(offset, offset + 3),
/*
values.slice(offset, offset + 3),
values.slice(offset + 4, offset + 7),
values.slice(offset + 8, offset + 11),
*/
],
centerpos: center,
length: .0200,
};
(function(c) {
bone.center = function() { return c; };
})(center);
data.fingers[i].bones[j] = bone;
}
for (j = 0; j < 4; j++) {
var bone = data.fingers[i].bones[j];
var nextbone = data.fingers[i].bones[j + 1];
if (nextbone) {
bone.length = pos.fromArray(bone.centerpos).distanceTo(new THREE.Vector3().fromArray(nextbone.centerpos));
}
var basepos = pos.fromArray(bone.centerpos).add(new THREE.Vector3().fromArray(bone.basis[2]).normalize().multiplyScalar(-bone.length/2));
data.fingers[i].positions[j+1] = basepos.toArray();
};
}
this.updateData(data, 1);
}
this.serializeMatrix = (function() {
var xdir = new THREE.Vector3(),
ydir = new THREE.Vector3(),
zdir = new THREE.Vector3(),
pos = new THREE.Vector3(),
pos2 = new THREE.Vector3();
var mat4 = new THREE.Matrix4();
var inverse = new THREE.Matrix4();
return function(matrix, transform) {
// Change order to match native: Y, Z, X
mat4.copy(matrix);
if (false && transform) {
inverse.getInverse(transform);
mat4.multiplyMatrices(inverse, matrix);
}
/*
var m = [];
for (var i = 0; i < 16; i++) {
m[i] = mat4.elements[i].toFixed(4);
}
var r = [
m[4], m[5], m[6], m[3],
m[0], m[1], m[2], m[11],
m[8], m[9], m[10], m[7],
m[12], m[13], m[14], m[15],
];
*/
pos.setFromMatrixPosition(mat4);
if (transform) {
//transform.worldToLocal(pos);
}
mat4.extractBasis(xdir, ydir, zdir);
xdir.normalize();
ydir.normalize();
zdir.normalize();
//inverse.makeBasis(xdir, ydir, zdir);
//inverse.makeBasis(xdir, zdir, ydir);
//inverse.makeBasis(ydir, xdir, zdir);
//inverse.makeBasis(ydir, zdir, xdir);
//inverse.makeBasis(zdir, xdir, ydir);
//inverse.makeBasis(zdir, ydir, xdir);
//inverse.setPosition(pos);
//console.log(xdir, ydir, zdir);
/*
inverse.set(
xdir.x, ydir.x, zdir.x, pos.x,
xdir.y, ydir.y, zdir.y, pos.y,
xdir.z, ydir.z, zdir.z, pos.z,
0, 0, 0, 1
);
*/
inverse.set(
xdir.x, xdir.y, xdir.z, 0,
ydir.x, ydir.y, ydir.z, 0,
zdir.x, zdir.y, zdir.x, 0,
pos.x, pos.y, pos.z, 1
);
return inverse.toArray().join(' ');
};
})();
this.getState = function(transform) {
var state = '';
state += this.serializeMatrix(this.palm.matrixWorld, transform);
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 4; j++) {
state += ' ' + this.serializeMatrix(this.fingers[i].phalanges[j].matrixWorld, transform);
}
}
return state;
}
}, elation.engine.things.generic);
elation.component.add('engine.things.leapmotion_finger', function() {
this.postinit = function() {
this.defineProperties({
hand: { type: 'object' }
});
}
this.createObject3D = function() {
var obj = new THREE.Object3D();
this.phalanges = [];
this.joints = [];
var material = this.hand.materials.bones;
var jointmaterial = this.hand.materials.joints;
var tipmaterial = this.hand.materials.tips;
var fingersizes = [0.011, 0.011, 0.00985, 0.00806, 0.00481, 0.00388];
var fingerSize = function(i) {
//return Math.pow(.075 / (i + 3), 1.1);
return fingersizes[i];
}
for (var i = 0; i < 4; i++) {
this.phalanges[i] = new THREE.Mesh(new THREE.CylinderGeometry(fingerSize(i), fingerSize(i+1), 1, 6, 2, false), material);
this.joints[i] = new THREE.Mesh(new THREE.SphereGeometry(fingerSize(i+1)), jointmaterial);
this.joints[i].castShadow = true;
this.joints[i].receiveShadow = true;
this.phalanges[i].geometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI/2));
this.phalanges[i].scale.z = .01;
this.phalanges[i].updateMatrix();
this.phalanges[i].matrixAutoUpdate = false;
this.phalanges[i].castShadow = true;
this.phalanges[i].receiveShadow = true;
obj.add(this.phalanges[i]);
if (i > 0) {
obj.add(this.joints[i]);
}
}
this.joints[4] = new THREE.Mesh(new THREE.SphereGeometry(fingerSize(5)), tipmaterial.clone());
this.fingertip = this.joints[4];
obj.add(this.joints[4]);
return obj;
}
this.updateData = function(data, scalefactor) {
for (var i = 0; i < data.bones.length; i++) {
var bone = data.bones[i];
var center = bone.center();
var length = Math.abs(Math.max(bone.length, .1) * scalefactor);
if (length > 0) {
//this.phalanges[i].scale.z = length * scalefactor;
} else if (this.phalanges[i].parent) {
this.objects['3d'].remove(this.phalanges[i]);
}
this.phalanges[i].matrix.set(
bone.basis[0][0], bone.basis[1][0], bone.basis[2][0] * length, center[0] * scalefactor,
bone.basis[0][1], bone.basis[1][1], bone.basis[2][1] * length, center[1] * scalefactor,
bone.basis[0][2], bone.basis[1][2], bone.basis[2][2] * length, center[2] * scalefactor,
0, 0, 0, 1
);
this.phalanges[i].updateMatrixWorld();
}
this.joints[0].position.fromArray(data.positions[0]).multiplyScalar(scalefactor);
this.joints[1].position.fromArray(data.positions[1]).multiplyScalar(scalefactor);
this.joints[2].position.fromArray(data.positions[2]).multiplyScalar(scalefactor);
this.joints[3].position.fromArray(data.positions[3]).multiplyScalar(scalefactor);
this.joints[4].position.fromArray(data.positions[4]).multiplyScalar(scalefactor);
this.refresh();
}
}, elation.engine.things.generic);
});