content/hifi-content/luis/hairMoveLib6.js
2022-02-14 02:04:11 +01:00

1363 lines
50 KiB
JavaScript

Script.include("/~/system/libraries/Xform.js");
(function(){
var SHOW_AVATAR = true;
var SHOW_DEBUG_SHAPES = false;
var SHOW_SOLID_SHAPES = false;
var USE_COLLISIONS = false;
var HAPTIC_TOUCH_STRENGTH = 0.25;
var HAPTIC_TOUCH_DURATION = 10.0;
var HAPTIC_SLOPE = 0.18;
var LEFT_HAND = 0;
var RIGHT_HAND = 1;
var HAPTIC_THRESHOLD = 10;
var JOINT_COLLISION_PREFIX = "joint_";
var HAND_COLLISION_PREFIX = "hand_";
var HAND_COLLISION_RADIUS = 0.03;
var HAND_TOUCHING_DISTANCE = 2.0;
var DUMMY_GROUP_NAME = "Extra"
var DUMMY_JOINT_COUNT = 8;
var DUMMY_JOINT_DISTANCE = 0.05;
// Vec3 Utils ////////////////////////////////////
var VEC3_UTIL_EPSILON = 0.000001;
var VEC3_UTIL_EPSILON_SQUARED = VEC3_UTIL_EPSILON * VEC3_UTIL_EPSILON;
var VEC3_UTIL_PI = 3.14159265358979;
var VEC3_UTIL_ALMOST_ONE= 1.0 - VEC3_UTIL_EPSILON;
var VEC3_UTIL_PI_OVER_TWO = 1.57079632679490;
function vec3(x,y,z) {return {x:x,y:y,z:z};}
function vec3Zero() {return {x:0,y:0,z:0};}
function lengthVec3(V) {return Math.sqrt(V.x*V.x+V.y*V.y+V.z*V.z);}
function addVec3s(A,B) {return {x:A.x+B.x,y:A.y+B.y,z:A.z+B.z};}
function subtractVec3s(A,B) {return {x:A.x-B.x,y:A.y-B.y,z:A.z-B.z};}
function scaleVec3(V, scale) {return {x:scale*V.x,y:scale*V.y,z:scale*V.z};}
function dotVec3s(A,B) {return A.x*B.x+A.y*B.y+A.z*B.z;}
function crossVec3s(A,B) {return {x:A.y*B.z-A.z*B.y,y:A.z*B.x-A.x*B.z,z:A.x*B.y-A.y*B.x};}
function distanceVec3s(A,B) {return Math.sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)+(A.z-B.z)*(A.z-B.z));}
function normalizeVec3(V) {
var L2=V.x*V.x+V.y*V.y+V.z*V.z;
if(L2<VEC3_UTIL_EPSILON_SQUARED) {return {x:V.x,y:V.y,z:V.z};}
var invL=1.0/Math.sqrt(L2);
return {x:invL*V.x,y:invL*V.y,z:invL*V.z};
}
function angleBetweenVec3s(A,B) {
var dot=dotVec3s(A,B);
if(dot<VEC3_UTIL_EPSILON) {return VEC3_UTIL_PI_OVER_TWO;}
var cosAngle=dot/(lengthVec3(A)*lengthVec3(B));
if(cosAngle>=VEC3_UTIL_ALMOST_ONE) {return 0.0;}
if(cosAngle<=-VEC3_UTIL_ALMOST_ONE) {return VEC3_UTIL_PI;}
return Math.acos(cosAngle);
}
// BEGIN quat utilities
function quat(x, y, z, w) {return {x:x,y:y,z:z,w:w};}
function quatIdentity() {return {x:0,y:0,z:0,w:1};}
function quatAngleAxis(angle,axis) {
var s=Math.sin(0.5*angle);
return {w:Math.cos(0.5*angle),x:s*axis.x,y:s*axis.y,z:s*axis.z};
}
function normalizeQuat(Q) {
var L2=Q.x*Q.x+Q.y*Q.y+Q.z*Q.z+Q.w*Q.w;
if(L2<VEC3_UTIL_EPSILON_SQUARED) {return {x:0.0,y:0.0,z:0.0,w:1};}
var invL=1.0/Math.sqrt(L2);
return {x:invL*Q.x,y:invL*Q.y,z:invL*Q.z,w:invL*Q.w};
}
function rotateVec3(Q,V) {
// from this page:
// http://www.mathworks.com/help/aeroblks/quaternionrotation.html
var Qxx=Q.x*Q.x;
var Qxy=Q.x*Q.y;
var Qxz=Q.x*Q.z;
var Qyy=Q.y*Q.y;
var Qyz=Q.y*Q.z;
var Qzz=Q.z*Q.z;
var Qwx=Q.w*Q.x;
var Qwy=Q.w*Q.y;
var Qwz=Q.w*Q.z;
return {
x: V.x*(1-2*(Qyy+Qzz))+V.y*2*(Qxy+Qwz)+V.z*2*(Qxz-Qwy),
y: V.x*2*(Qxy-Qwz)+V.y*(1-2*(Qxx+Qzz))+V.z*2*(Qyz+Qwx),
Z: V.x*2*(Qxz+Qwy)+V.y*2*(Qyz-Qwx)+V.z*(1-2*(Qxx+Qyy))};
}
function combineQuats(Q, R) {
// from this page:
// http://mathworld.wolfram.com/Quaternion.html
return {
w:Q.w*R.w-Q.x*R.x-Q.y*R.y-Q.z*R.z,
x:Q.w*R.x+Q.x*R.w+Q.y*R.z-Q.z*R.y,
y:Q.w*R.y-Q.x*R.z+Q.y*R.w+Q.z*R.x,
z:Q.w*R.z+Q.x*R.y-Q.y*R.x+Q.z*R.w};
}
function dotQuats(Q,R) {return Q.w*R.w+Q.x*R.x+Q.y*R.y+Q.z*R.z;}
function quatAngle(Q) {return 2*Math.acos(Q.w);}
function quatAxis(Q) {
var s=Math.sqrt(1-Q.w*Q.w);
if(s<VEC3_UTIL_EPSILON_SQUARED) {
// Q is identity, so no axis makes sense
return {x:0,y:0,z:0};
}
return {x:Q.x,y:Q.y,z:Q.z};
}
function createQuatIdentity() {return {w:1,x:0,y:0,z:0};}
function createQuatAngleAxis(angle,axis) {
var s=Math.sin(0.5*angle);
return {w:Math.cos(0.5*angle),x:s*axis.x,y:s*axis.y,z:s*axis.z};
}
function negateQuat(Q) {return {w:-Q.w,x:-Q.x,y:-Q.y,z:-Q.z};}
function inverseQuat(Q) {return {w:-Q.w,x:Q.x,y:Q.y,z:Q.z};}
function rotationBetween(orig, dest) {
var v1 = normalizeVec3(orig);
var v2 = normalizeVec3(dest);
var cosTheta = dotVec3s(v1, v2);
var rotationAxis;
if(cosTheta >= 1 - VEC3_UTIL_EPSILON){
return quatIdentity();
}
if(cosTheta < -1 + VEC3_UTIL_EPSILON)
{
// special case when vectors in opposite directions :
// there is no "ideal" rotation axis
// So guess one; any will do as long as it's perpendicular to start
// This implementation favors a rotation around the Up axis (Y),
// since it's often what you want to do.
rotationAxis = crossVec3s({x: 0, y: 0, z: 1}, v1);
if(lengthVec3(rotationAxis) < VEC3_UTIL_EPSILON) { // bad luck, they were parallel, try again!
rotationAxis = crossVec3s({x:1, y:0, z:0}, v1);
}
rotationAxis = normalizeVec3(rotationAxis);
return createQuatAngleAxis(VEC3_UTIL_PI, rotationAxis);
}
// Implementation from Stan Melax's Game Programming Gems 1 article
rotationAxis = crossVec3s(v1, v2);
var s = Math.sqrt((1 + cosTheta) * 2);
var invs = 1 / s;
return {
w: s * 0.5,
x: rotationAxis.x * invs,
y: rotationAxis.y * invs,
z: rotationAxis.z * invs,
}
}
// END Quat utilities
// Joint groups by keyword
var JOINT_FILTER_KEYWORDS = ["Hair", "Skirt", "Breast", DUMMY_GROUP_NAME];
var JOINT_FILTER_DATA = {
"Hair": {"stiffness": 0.0, "gravity": -0.0096 ,"damping": 0.85, "inertia": 0.8, "delta": 0.55, "friction": 0.0, "radius": 0.03},
"Skirt": {"stiffness": 0.0, "gravity": -0.0096 ,"damping": 0.85, "inertia": 0.25, "delta": 0.45, "friction": 0.0, "radius": 0.01},
"Breast": {"stiffness": 1, "gravity": -0.0096 ,"damping": 0.65, "inertia": 0.8, "delta": 0.45, "friction": 0.0, "radius": 0.01}
};
JOINT_FILTER_DATA[DUMMY_GROUP_NAME] = {"stiffness": 0.0, "gravity": -0.0096 ,"damping": 0.85, "inertia": 0.8, "delta": 0.55, "friction": 0.0, "radius": 0.01};
var COLLISION_SHAPES = {
// sphere params type, radius, offset
// cube params type, dimensions, offset
/*
"Head": {type: "sphere", radius: 0.1, offset: {x: 0, y: 0.05, z: 0.03}},
"Spine2": {type: "sphere",radius: 0.07, attenuation: 0.03, offset: {x: 0, y: 0.08, z: 0.00}}
*/
"Head": {type: "sphere", radius: 0.09, offset: {x: 0, y: 0.04, z: 0.05}},
//"LeftShoulder": {type: "sphere", radius: 0.05, offset: {x: 0, y: 0.05, z: 0.00}},
//"RightShoulder": {type: "sphere", radius: 0.05, offset: {x: 0, y: 0.05, z: 0.00}},
"RightArm": {type: "sphere", radius: 0.05, offset: {x: 0.0, y: 0.02, z: 0.00}},
"LeftArm": {type: "sphere", radius: 0.05, offset: {x: 0.0, y: 0.02, z: 0.00}},
"Spine2": {type: "sphere", radius: 0.14, offset: {x: 0, y: 0.2, z: 0.00}},
//"Spine1": {type: "sphere", radius: 0.09, offset: {x: 0, y: 0.02, z: 0.00}}
};
var FlowDebug = function() {
var self = this;
this.debugLines = {};
this.debugSpheres = {};
this.debugCubes = {};
this.showDebugShapes = false;
this.showSolidShapes = false;
this.setDebugCube = function(cubeName, cubePosition, cubeRotation, cubeDimensions, shapeColor, forceRendering) {
var doRender = self.showDebugShapes || forceRendering;
if (!doRender) return;
var position = cubePosition ? cubePosition : {x: 0, y: 0, z: 0};
var rotation = cubeRotation ? cubeRotation : {x: 0, y: 0, z: 0, w: 0};
var dimensions = cubeDimensions ? cubeDimensions : {x: 1, y: 1, z: 1};
var color = shapeColor ? shapeColor : { red: 0, green: 255, blue: 255 };
if (self.debugCubes[cubeName] != undefined) {
Overlays.editOverlay(self.debugCubes[cubeName], {
position: position,
rotation: rotation,
dimensions: dimensions,
color: color,
solid: self.showSolidShapes,
visible: true
});
} else {
self.debugCubes[cubeName] = Overlays.addOverlay("cube", {
position: position,
rotation: rotation,
dimensions: dimensions,
color: color,
solid: self.showSolidShapes,
visible: true
});
}
}
this.setDebugLine = function(lineName, startPosition, endPosition, shapeColor, forceRendering) {
var doRender = self.showDebugShapes || forceRendering;
if (!doRender) return;
var start = startPosition ? startPosition : {x: 0, y: 0, z: 0};
var end = endPosition ? endPosition : {x: 0, y: 1, z: 0};
var color = shapeColor ? shapeColor : { red: 0, green: 255, blue: 255 };
if (self.debugLines[lineName] != undefined) {
Overlays.editOverlay(self.debugLines[lineName], {
color: color,
start: start,
end: end,
visible: true
});
} else {
self.debugLines[lineName] = Overlays.addOverlay("line3d", {
color: color,
start: start,
end: end,
visible: true
});
}
}
this.setDebugSphere = function(sphereName, pos, diameter, shapeColor, forceRendering) {
var doRender = self.showDebugShapes || forceRendering;
if (!doRender) return;
var scale = diameter ? diameter : 0.01;
var color = shapeColor ? shapeColor : { red: 255, green: 0, blue: 255 };
if (self.debugSpheres[sphereName] != undefined) {
Overlays.editOverlay(self.debugSpheres[sphereName], {
color: color,
position: pos,
scale: {x:scale, y:scale, z:scale},
solid: self.showSolidShapes,
visible: true
});
} else {
self.debugSpheres[sphereName] = Overlays.addOverlay("sphere", {
color: color,
position: pos,
scale: {x:scale, y:scale, z:scale},
solid: self.showSolidShapes,
visible: true
});
}
}
this.deleteSphere = function(name) {
Overlays.deleteOverlay(self.debugSpheres[name]);
}
this.deleteLine = function(name) {
Overlays.deleteOverlay(self.debugLines[name]);
}
this.deleteCube = function(name) {
Overlays.deleteOverlay(self.debugCubes[name]);
}
this.cleanup = function() {
for (lineName in self.debugLines) {
self.deleteLine(lineName);
}
for (sphereName in self.debugSpheres) {
self.deleteSphere(sphereName);
}
for (cubeName in self.debugCubes) {
self.deleteCube(cubeName);
}
self.debugLines = {};
self.debugSpheres = {};
self.debugCubes = {};
}
this.setVisible = function(isVisible) {
self.showDebugShapes = isVisible;
for (var lineName in self.debugLines) {
Overlays.editOverlay(self.debugLines[lineName], {
visible: isVisible
});
}
for (var sphereName in self.debugSpheres) {
Overlays.editOverlay(self.debugSpheres[sphereName], {
visible: isVisible
});
}
}
this.setSolid = function(isSolid) {
self.showSolidShapes = isSolid;
for (var lineName in self.debugLines) {
Overlays.editOverlay(self.debugLines[lineName], {
solid: isSolid
});
}
for (var sphereName in self.debugSpheres) {
Overlays.editOverlay(self.debugSpheres[sphereName], {
solid: isSolid
});
}
}
}
var FlowHandSystem = function() {
var self = this;
this.HANDS_COLLISION_JOINTS = ["RightHandMiddle1", "RightHandMiddle3", "LeftHandMiddle1", "LeftHandMiddle3","RightHandPinky3", "LeftHandPinky3"];
this.avatarIds = [];
this.avatarHands = [];
this.lastAvatarCount = 0;
this.leftTriggerValue = 0;
this.rightTriggerValue = 0;
this.isLeftHandTouching = false;
this.leftHandTouchDelta = 0;
this.leftHandTouchThreshold = 0;
this.isRightHandTouching = false;
this.rightHandTouchDelta = 0;
this.rightHandTouchThreshold = 0;
this.getNearbyAvatars = function(distance) {
return AvatarList.getAvatarIdentifiers().filter(function(avatarID){
if (!avatarID) {
return false;
}
var avatar = AvatarList.getAvatar(avatarID);
var avatarPosition = avatar && avatar.position;
if (!avatarPosition) {
return false;
}
return distanceVec3s(avatarPosition, MyAvatar.position) < distance;
});
}
this.update = function() {
var nearbyAvatars = self.getNearbyAvatars(HAND_TOUCHING_DISTANCE);
nearbyAvatars.push(MyAvatar.SELF_ID);
nearbyAvatars.forEach(function(avatarID) {
var avatar = AvatarList.getAvatar(avatarID);
var avatarIndex = self.avatarIds.indexOf(avatarID);
if (avatarIndex === -1) {
var newHands = {};
for (var i = 0; i < self.HANDS_COLLISION_JOINTS.length; i++) {
var side = self.HANDS_COLLISION_JOINTS[i];
var jointId = avatar.getJointIndex(side);
var position = avatar.getJointPosition(jointId);
var name = avatarID + "_" + HAND_COLLISION_PREFIX + side;
var handCollisionSettings = {type: "sphere", radius: HAND_COLLISION_RADIUS, offset: {x: 0, y: 0, z: 0}, avatarId: avatarID};
newHands[side] = new FlowCollisionSphere(name, jointId, side, handCollisionSettings);
}
self.avatarHands.push(newHands);
avatarIndex = self.avatarIds.length;
self.avatarIds.push(avatarID);
}
for (var i = 0; i < self.HANDS_COLLISION_JOINTS.length; i++) {
var side = self.HANDS_COLLISION_JOINTS[i];
self.avatarHands[avatarIndex][side].update();
}
});
if (nearbyAvatars.length < self.lastAvatarCount) {
var avatarsToUntrack = [];
for (var i = 0; i < self.avatarIds.length; i++) {
var index = nearbyAvatars.indexOf(self.avatarIds[i]);
if (index == -1) {
avatarsToUntrack.push(i);
}
}
avatarsToUntrack.sort();
for (var i = 0; i < avatarsToUntrack.length; i++) {
self.avatarIds.splice(avatarsToUntrack[i]-i, 1);
self.avatarHands.splice(avatarsToUntrack[i]-i, 1);
}
}
self.lastAvatarCount = nearbyAvatars.length;
}
this.computeCollision = function(collisions) {
var collisionData = new FlowCollisionData();
if (collisions.length > 1) {
for (var i = 0; i < collisions.length; i++) {
collisionData.offset += collisions[i].offset
collisionData.normal = addVec3s(collisionData.normal, scaleVec3(collisions[i].normal, collisions[i].distance));
collisionData.position = addVec3s(collisionData.position, collisions[i].position);
collisionData.radius += collisions[i].radius
collisionData.distance += collisions[i].distance
}
collisionData.offset = collisionData.offset/collisions.length;
collisionData.radius = lengthVec3(collisionData.normal)/2;
collisionData.normal = normalizeVec3(collisionData.normal);
collisionData.position = scaleVec3(collisionData.position, 1/collisions.length);
collisionData.distance = collisionData.distance/collisions.length;
} else if (collisions.length == 1){
collisionData = collisions[0];
}
collisionData.collisionCount = collisions.length;
return collisionData;
}
this.manageHapticPulse = function() {
if (self.isLeftHandTouching) {
self.leftHandTouchDelta += HAPTIC_SLOPE;
self.leftHandTouchThreshold = 0;
Controller.triggerHapticPulse(HAPTIC_TOUCH_STRENGTH/self.leftHandTouchDelta, HAPTIC_TOUCH_DURATION, LEFT_HAND);
} else {
if (self.leftHandTouchThreshold++ > HAPTIC_THRESHOLD) {
self.leftHandTouchDelta = 0;
}
}
if (self.isRightHandTouching) {
self.rightHandTouchDelta += HAPTIC_SLOPE;
self.rightHandTouchThreshold = 0;
Controller.triggerHapticPulse(HAPTIC_TOUCH_STRENGTH/self.rightHandTouchDelta, HAPTIC_TOUCH_DURATION, RIGHT_HAND);
} else {
if (self.rightHandTouchThreshold++ > HAPTIC_THRESHOLD) {
self.rightHandTouchDelta = 0;
}
}
}
this.checkThreadCollisions = function(thread) {
var threadCollisionData = Array(thread.joints.length);
for (var i = 0; i < threadCollisionData.length; i++) {
threadCollisionData[i] = [];
}
self.isRightHandTouching = false;
self.isLeftHandTouching = false;
for (var i = 0; i < self.avatarHands.length; i++) {
for (var j = 0; j < self.HANDS_COLLISION_JOINTS.length; j++) {
side = self.HANDS_COLLISION_JOINTS[j];
var rootCollision = self.avatarHands[i][side].checkCollision(thread.positions[0], thread.radius);
var collisionData = [rootCollision];
var tooFar = rootCollision.distance > (thread.length + rootCollision.radius);
if (!tooFar) {
for (var k = 1; k < thread.joints.length; k++) {
var prevCollision = collisionData[k-1];
var nextCollision = self.avatarHands[i][side].checkCollision(thread.positions[k], thread.radius);
collisionData.push(nextCollision);
isTouching = false;
if (prevCollision.offset > 0) {
if (k == 1) {
threadCollisionData[k-1].push(prevCollision);
isTouching = true;
}
} else if (nextCollision.offset > 0) {
threadCollisionData[k].push(nextCollision);
isTouching = true;
} else {
var segmentCollision = self.avatarHands[i][side].checkSegmentCollision(thread.positions[k-1], thread.positions[k], prevCollision, nextCollision);
if (segmentCollision.offset > 0) {
threadCollisionData[k-1].push(segmentCollision);
threadCollisionData[k].push(segmentCollision);
isTouching = true;
}
}
if (isTouching) {
if (side.indexOf("RightHand") > -1) {
self.isRightHandTouching = true;
} else {
self.isLeftHandTouching = true;
}
}
}
}
}
}
self.manageHapticPulse();
var collisionResult = [];
for (var i = 0; i < thread.joints.length; i++) {
collisionResult.push(self.computeCollision(threadCollisionData[i]));
}
return collisionResult;
}
this.setRightTriggerValue = function(value) {
self.rightTriggerValue = value;
}
this.setLeftTriggerValue = function(value) {
self.leftTriggerValue = value;
}
}
var FlowCollisionSystem = function() {
var self = this;
this.collisionSpheres = [];
this.collisionCubes = [];
this.addCollisionSphere = function(name, jointIndex, jointName, settings) {
self.collisionSpheres.push(new FlowCollisionSphere(name, jointIndex, jointName, settings));
}
this.addCollisionCube = function(name, jointIndex, jointName, settings) {
self.collisionCubes.push(new FlowCollisionCube(name, jointIndex, jointName, settings));
}
this.addCollisionShape = function(name, jointIndex, jointName, settings) {
switch(settings.type) {
case "sphere":
self.addCollisionSphere(name, jointIndex, jointName, settings);
break;
case "cube":
self.addCollisionCube(name, jointIndex, jointName, settings);
break;
}
}
this.update = function() {
for (var i = 0; i < self.collisionCubes.length; i++) {
self.collisionCubes[i].update();
}
for (var i = 0; i < self.collisionSpheres.length; i++) {
self.collisionSpheres[i].update();
}
}
this.computeCollision = function(collisions) {
var collisionData = new FlowCollisionData();
if (collisions.length > 1) {
for (var i = 0; i < collisions.length; i++) {
collisionData.offset += collisions[i].offset
collisionData.normal = addVec3s(collisionData.normal, scaleVec3(collisions[i].normal, collisions[i].distance));
collisionData.position = addVec3s(collisionData.position, collisions[i].position);
collisionData.radius += collisions[i].radius
collisionData.distance += collisions[i].distance
}
collisionData.offset = collisionData.offset/collisions.length;
collisionData.radius = lengthVec3(collisionData.normal)/2;
collisionData.normal = normalizeVec3(collisionData.normal);
collisionData.position = scaleVec3(collisionData.position, 1/collisions.length);
collisionData.distance = collisionData.distance/collisions.length;
} else if (collisions.length == 1){
collisionData = collisions[0];
}
collisionData.collisionCount = collisions.length;
return collisionData;
}
this.checkThreadCollisions = function(thread, checkSegments) {
var threadCollisionData = Array(thread.joints.length);
for (var i = 0; i < threadCollisionData.length; i++) {
threadCollisionData[i] = [];
}
for (var j = 0; j < self.collisionSpheres.length; j++) {
var rootCollision = self.collisionSpheres[j].checkCollision(thread.positions[0], thread.radius);
var collisionData = [rootCollision];
var tooFar = rootCollision.distance > (thread.length + rootCollision.radius);
if (!tooFar) {
if (checkSegments) {
for (var i = 1; i < thread.joints.length; i++) {
var prevCollision = collisionData[i-1];
var nextCollision = self.collisionSpheres[j].checkCollision(thread.positions[i], thread.radius);
collisionData.push(nextCollision);
if (prevCollision.offset > 0) {
if (i == 1) {
threadCollisionData[i-1].push(prevCollision);
}
} else if (nextCollision.offset > 0) {
threadCollisionData[i].push(nextCollision);
} else {
var segmentCollision = self.collisionSpheres[j].checkSegmentCollision(thread.positions[i-1], thread.positions[i], prevCollision, nextCollision);
if (segmentCollision.offset > 0) {
threadCollisionData[i-1].push(segmentCollision);
threadCollisionData[i].push(segmentCollision);
}
}
}
} else {
if (rootCollision.offset > 0) {
threadCollisionData[0].push(rootCollision);
}
for (var i = 1; i < thread.joints.length; i++) {
var nextCollision = self.collisionSpheres[j].checkCollision(thread.positions[i], thread.radius);
if (nextCollision.offset > 0) {
threadCollisionData[i].push(nextCollision);
}
}
}
}
}
var collisionResult = [];
for (var i = 0; i < thread.joints.length; i++) {
collisionResult.push(self.computeCollision(threadCollisionData[i]));
}
return collisionResult;
}
this.modifyCollision = function(jointName, parameter, value) {
var jointIndex = MyAvatar.getJointIndex(jointName);
for (var i = 0; i < self.collisionSpheres.length; i++) {
if (self.collisionSpheres[i].jointIndex == jointIndex) {
switch(parameter) {
case "radius": {
self.collisionSpheres[i].radius = value;
break;
}
case "offset": {
self.collisionSpheres[i].offset.y = value;
break;
}
}
return;
}
}
}
this.getCollisionData = function() {
var collisionData = [];
for (var i = 0; i < self.collisionSpheres.length; i++) {
var data = {type: "sphere", radius: self.collisionSpheres[i].radius, offset: self.collisionSpheres[i].offset, joint: self.collisionSpheres[i].jointIndex, jointName: "Joint : " + self.collisionSpheres[i].jointName};
collisionData.push(data);
}
return collisionData;
}
}
var FlowCollisionData = function(offset, position, radius, normal, distance) {
this.collisionCount = 0;
this.offset = offset != undefined ? offset : 0;
this.position = position != undefined ? position : {x: 0, y: 0, z: 0};
this.radius = radius != undefined ? radius : 0;
this.normal = normal != undefined ? normal : {x: 0, y: 0, z: 0};
this.distance = distance != undefined ? distance : 0;
}
var FlowCollisionSphere = function(name, jointIndex, jointName, settings) {
var self = this;
this.name = name;
this.jointIndex = jointIndex;
this.jointName = jointName;
this.radius = settings.radius;
this.offset = settings.offset;
this.attenuation = settings.attenuation;
this.avatarId = settings.avatarId;
this.position = {x:0, y:0, z:0};
this.update = function() {
if (self.avatarId != undefined){
var avatar = AvatarList.getAvatar(self.avatarId);
self.position = avatar.getJointPosition(self.jointIndex);
} else {
self.position = MyAvatar.jointToWorldPoint(self.offset, self.jointIndex);
}
collisionDebug.setDebugSphere(self.name, self.position, 2*self.radius, {red: 200, green: 10, blue: 50});
if (self.attenuation && self.attenuation > 0) {
collisionDebug.setDebugSphere(self.name + "_att", self.position, 2*(self.radius + self.attenuation), {red: 120, green: 200, blue: 50});
}
}
this.checkCollision = function(point, radius) {
var centerToJoint = subtractVec3s(point, self.position);
var distance = lengthVec3(centerToJoint) - radius;
var offset = self.radius - distance;
var collisionData = new FlowCollisionData(offset, self.position, self.radius, normalizeVec3(centerToJoint), distance);
return collisionData;
}
this.checkSegmentCollision = function(point1, point2, pointCollision1, pointCollision2) {
var collisionData = new FlowCollisionData();
var segment = subtractVec3s(point2, point1);
var segmentLength = lengthVec3(segment);
var maxDistance = Math.sqrt(Math.pow(pointCollision1.radius,2) + Math.pow(segmentLength,2));
if (pointCollision1.distance < maxDistance && pointCollision2.distance < maxDistance) {
var segmentPercent = pointCollision1.distance/(pointCollision1.distance + pointCollision2.distance);
var collisionPoint = addVec3s(point1, scaleVec3(segment, segmentPercent))
var centerToSegment = subtractVec3s(collisionPoint, self.position);
var distance = lengthVec3(centerToSegment);
if (distance < self.radius) {
var offset = self.radius - distance;
collisionData = new FlowCollisionData(offset, self.position, self.radius, normalizeVec3(centerToSegment), distance);
}
}
return collisionData;
}
}
var FlowCollisionCube = function(name, jointIndex, settings) {
var self = this;
this.name = name;
this.jointIndex = jointIndex;
this.dimensions = settings.dimensions;
this.offset = settings.offset;
this.attenuation = settings.attenuation;
this.avatarId = settings.avatarId;
this.position = {x:0, y:0, z:0};
this.rotation = {x:0, y:0, z:0, w:0};
this.update = function() {
if (self.avatarId != undefined){
var avatar = AvatarList.getAvatar(self.avatarId);
self.position = avatar.getJointPosition(self.jointIndex);
self.rotation = avatar.getJointRotation(self.jointIndex);
} else {
self.position = MyAvatar.jointToWorldPoint(offset, jointIndex);
self.rotation = MyAvatar.jointToWorldRotation({x:0, y:0, z:0, w:0}, jointIndex);
}
collisionDebug.setDebugCube(self.name, self.position, self.rotation, self.dimensions, {red: 200, green: 10, blue: 50});
}
this.checkCollision = function(point, radius) {
var localPoint = MyAvatar.worldToJointPoint(point, self.jointIndex);
var localPosition = MyAvatar.worldToJointPoint(self.position, self.jointIndex);
var centerToJoint = subtractVec3s(localPoint, localPosition);
var offsets = { x: (self.dimensions.x/2) - Math.abs(centerToJoint.x),
y: (self.dimensions.y/2) - Math.abs(centerToJoint.y),
z: (self.dimensions.z/2) - Math.abs(centerToJoint.z)};
var radius = 0;
var normal = {x: 0, y: 0, z: 0};
var position = {x: 0, y: 0, z: 0};
var offset = 0;
if (offsets.x > 0 && offsets.y > 0 && offsets.z > 0) {
var offsetOrder = [{"coordinate": "x", "value": offsets.x}, {"coordinate": "y", "value": offsets.y}, {"coordinate": "z", "value": offsets.z}];
offsetOrder.sort(function(a, b){
return a.value - b.value;
});
switch (offsetOrder[0].coordinate) {
case "x" :
normal = {x: centerToJoint.x > 0 ? 1 : -1, y: 0, z: 0};
radius = self.dimensions.x/2;
position = addVec3s(self.offset, {x: 0, y: centerToJoint.y, z: centerToJoint.z});
offset = offsets.x;
break;
case "y" :
normal = {x: 0, y: centerToJoint.y > 0 ? 1 : -1, z: 0};
radius = self.dimensions.y/2;
position = addVec3s(self.offset, {x: centerToJoint.x, y: 0, z: centerToJoint.z});
offset = offsets.y;
break;
case "z" :
normal = {x: 0, y: 0, z: centerToJoint.z > 0 ? 1 : -1};
radius = self.dimensions.z/2;
position = addVec3s(self.offset, {x: centerToJoint.x, y: centerToJoint.y, z: 0});
offset = offsets.z;
break;
}
normal = MyAvatar.jointToWorldDirection(normal, self.jointIndex);
position = MyAvatar.jointToWorldPoint(position, self.jointIndex);
}
return new FlowCollisionData(offset, position, radius, normal, offset);
}
}
var FlowNode = function(initialPosition, settings) {
var self = this;
this.radius = settings.radius;
this.gravity = settings.gravity;
this.damping = settings.damping;
this.inertia = settings.inertia;
this.delta = settings.delta;
this.friction = settings.friction;
this.initialPosition = initialPosition;
this.previousPosition = this.initialPosition;
this.currentPosition = this.initialPosition;
this.previousCollision = new FlowCollisionData();
this.currentVelocity = {x:0, y:0, z:0};
this.previousVelocity = {x:0, y:0, z:0};
this.acceleration = {x:0, y:0, z:0};
this.anchored = false;
this.colliding = false;
this.collision;
this.update = function(accelerationOffset) {
self.acceleration = {x: 0, y: self.gravity, z: 0};
self.previousVelocity = self.currentVelocity;
self.currentVelocity = subtractVec3s(self.currentPosition, self.previousPosition);
self.previousPosition = self.currentPosition;
if (!self.anchored) {
// Add inertia
var centrifugeVector = normalizeVec3(subtractVec3s(self.previousVelocity, self.currentVelocity));
self.acceleration = addVec3s(self.acceleration, scaleVec3(centrifugeVector, self.inertia * lengthVec3(self.currentVelocity)));
// Add offset
self.acceleration = addVec3s(self.acceleration, accelerationOffset);
// Calculate new position
self.currentPosition = addVec3s(addVec3s(self.currentPosition, scaleVec3(self.currentVelocity, self.damping)), scaleVec3(self.acceleration, Math.pow(self.delta, 2)));
} else {
self.acceleration = {x:0, y:0, z:0};
self.currentVelocity = {x:0, y:0, z:0};
}
}
this.solve = function(constrainPoint, maxDistance, collision) {
var constrainVector = subtractVec3s(self.currentPosition, constrainPoint);
var difference = maxDistance/lengthVec3(constrainVector);
self.currentPosition = difference < 1.0 ? addVec3s(constrainPoint, scaleVec3(constrainVector, difference)) : self.currentPosition;
self.colliding = collision && (collision.offset > 0);
self.collision = collision;
if (self.colliding) {
var position = addVec3s(self.currentPosition, scaleVec3(collision.normal, collision.offset));
if (self.previousCollision) {
var frictionPosition = addVec3s(self.currentPosition, scaleVec3(self.previousCollision.normal, collision.offset));
self.currentPosition = addVec3s(scaleVec3(position, (1 - self.friction)), scaleVec3(frictionPosition, self.friction));
} else {
self.currentPosition = position;
}
self.previousCollision = collision;
} else {
self.previousCollision = undefined;
}
}
this.apply = function(name, forceRendering) {
jointDebug.setDebugSphere(name, self.currentPosition, 2*self.radius, {red: self.collision && self.collision.collisionCount > 1 ? 0 : 255, green:self.colliding ? 0 : 255, blue:0}, forceRendering);
}
}
var FlowJoint = function(index, parentIndex, name, group, settings){
var self = this;
this.index = index;
this.name = name;
this.group = group;
this.parentIndex = parentIndex;
this.childIndex = -1;
this.isDummy = false;
this.initialPosition = MyAvatar.getJointPosition(index);
this.initialXform = new Xform(MyAvatar.getJointRotation(index), MyAvatar.getJointTranslation(index));
this.currentRotation;
this.recoveryPosition;
this.node = new FlowNode(self.initialPosition, settings);
this.stiffness = settings.stiffness;
this.translationDirection = normalizeVec3(this.initialXform.pos);
this.length = lengthVec3(subtractVec3s(this.initialPosition, MyAvatar.getJointPosition(self.parentIndex)));
this.update = function () {
var accelerationOffset = {x: 0, y: 0, z: 0};
if (!self.isDummy && self.recoveryPosition) {
var recoveryVector = subtractVec3s(self.recoveryPosition, self.node.currentPosition);
var accelerationOffset = scaleVec3(recoveryVector, Math.pow(self.stiffness, 3));
}
self.node.update(accelerationOffset);
if (self.node.anchored) {
if (!self.isDummy) {
self.node.currentPosition = MyAvatar.getJointPosition(self.index);
} else {
self.node.currentPosition = MyAvatar.getJointPosition(self.parentIndex);
}
}
}
this.solve = function(collision) {
var parentPosition = flowJointData[self.parentIndex] ? flowJointData[self.parentIndex].node.currentPosition : MyAvatar.getJointPosition(self.parentIndex);
self.node.solve(parentPosition, self.length, collision);
}
this.apply = function() {
var parentJoint;
if (self.currentRotation) {
MyAvatar.setJointRotation(self.index, self.currentRotation);
//MyAvatar.setJointTranslation(self.index, scaleVec3(normalizeVec3(self.initialXform.pos, self.currentLength)));
}
//jointDebug.setDebugSphere(self.name + "_deb", self.recoveryPosition, 0.02, {red: 255, green:100, blue:100}, false);
self.node.apply(self.name, self.isDummy);
}
}
var FlowJointDummy = function(initialPosition, index, parentIndex, childIndex, settings) {
var group = DUMMY_GROUP_NAME;
var name = DUMMY_GROUP_NAME + "_" + index;
FlowJoint.call(this, index, parentIndex, name, group, settings);
this.isDummy = true;
this.childIndex = childIndex;
this.initialPosition = initialPosition;
this.node = new FlowNode(initialPosition, settings);
this.length = DUMMY_JOINT_DISTANCE;
}
var FlowThread = function(root) {
var self = this;
this.joints = [];
this.positions = [];
this.radius = 0;
this.length = 0;
this.computeThread = function(rootIndex) {
var parentIndex = rootIndex;
var childIndex = flowJointData[parentIndex].childIndex;
var indexes = [parentIndex];
for (var i = 0; i < flowSkeleton.length; i++) {
if (childIndex > -1) {
indexes.push(childIndex);
childIndex = flowJointData[childIndex].childIndex;
} else {
break;
}
}
for (var i = 0; i < indexes.length; i++) {
var index = indexes[i];
self.joints.push(index);
if (i > 0) {
self.length += flowJointData[index].length;
}
}
}
this.computeRecovery = function() {
var parentIndex = self.joints[0];
var parentJoint = flowJointData[parentIndex];
parentJoint.recoveryPosition = parentJoint.node.currentPosition;
var parentRotation = combineQuats(MyAvatar.jointToWorldRotation(quatIdentity(), parentJoint.parentIndex), parentJoint.initialXform.rot);
for (var i = 1; i < self.joints.length; i++) {
var joint = flowJointData[self.joints[i]];
var rotation = i === 1 ? parentRotation : combineQuats(rotation, parentJoint.initialXform.rot);
joint.recoveryPosition = addVec3s(parentJoint.recoveryPosition, Vec3.multiplyQbyV(rotation, scaleVec3(joint.initialXform.pos, 0.01)));
parentJoint = joint;
}
}
this.update = function() {
self.positions = [];
self.radius = flowJointData[self.joints[0]].node.radius;
self.computeRecovery();
for (var i = 0; i < self.joints.length; i++){
var joint = flowJointData[self.joints[i]];
joint.update();
self.positions.push(joint.node.currentPosition);
}
}
this.solve = function(useCollisions) {
if (useCollisions) {
var handCollisions = handSystem.checkThreadCollisions(self);
var bodyCollisions = collisionSystem.checkThreadCollisions(self);
var handTouchedJoint = -1;
for (var i = 0; i < self.joints.length; i++) {
var index = self.joints[i];
if (bodyCollisions[i].offset > 0) {
flowJointData[index].solve(bodyCollisions[i]);
} else {
handTouchedJoint = (handCollisions[i].offset > 0) ? i : -1;
flowJointData[index].solve(handCollisions[i]);
}
}
if (handTouchedJoint > 1) {
console.log("handTouched: "+ handTouchedJoint);
//for (var i = handTouchedJoint; i > 1; i--) {
//var joint1 = flowJointData[self.joints[handTouchedJoint]];
//var joint2 = flowJointData[self.joints[handTouchedJoint-2]];
//var middleJoint = flowJointData[self.joints[handTouchedJoint-1]];
//var middlePos = scaleVec3(addVec3s(joint1.node.currentPosition, joint2.node.currentPosition), 0.5);
//flowJointData[self.joints[handTouchedJoint-1]].node.currentPosition = middlePos;//scaleVec3(addVec3s(middleJoint.node.currentPosition, middlePos), 0.5);
//}
}
} else {
for (var i = 0; i < self.joints.length; i++) {
var index = self.joints[i];
flowJointData[index].solve(new FlowCollisionData());
}
}
}
this.computeJointRotations = function() {
var rootIndex = flowJointData[self.joints[0]].parentIndex;
var rootFramePositions = [];
for (var i = 0; i < self.joints.length; i++){
rootFramePositions.push(MyAvatar.worldToJointPoint(flowJointData[self.joints[i]].node.currentPosition, rootIndex));
}
var pos0 = rootFramePositions[0];
var pos1 = rootFramePositions[1];
var joint0 = flowJointData[self.joints[0]];
var joint1 = flowJointData[self.joints[1]];
var initial_pos1 = addVec3s(pos0, Vec3.multiplyQbyV(joint0.initialXform.rot, scaleVec3(joint1.initialXform.pos, 0.01)));
var vec0 = subtractVec3s(initial_pos1, pos0);
var vec1 = subtractVec3s(pos1, pos0);
var delta = rotationBetween(vec0, vec1);
joint0.currentRotation = combineQuats(delta, joint0.initialXform.rot);
for (var i = 1; i < self.joints.length-1; i++){
var nextJoint = flowJointData[self.joints[i+1]];
for (var j = i; j < self.joints.length; j++){
rootFramePositions[j] = Vec3.multiplyQbyV(inverseQuat(joint0.currentRotation), subtractVec3s(rootFramePositions[j], scaleVec3(joint0.initialXform.pos, 0.01)));
}
pos0 = rootFramePositions[i];
pos1 = rootFramePositions[i+1];
initial_pos1 = addVec3s(pos0, Vec3.multiplyQbyV(joint1.initialXform.rot, scaleVec3(nextJoint.initialXform.pos, 0.01)));
vec0 = subtractVec3s(initial_pos1, pos0);
vec1 = subtractVec3s(pos1, pos0);
delta = rotationBetween(vec0, vec1);
joint1.currentRotation = combineQuats(delta, joint1.initialXform.rot);
joint0 = joint1;
joint1 = nextJoint;
}
}
this.apply = function() {
self.computeJointRotations();
for (var i = 0; i < self.joints.length; i++){
var joint = flowJointData[self.joints[i]];
var parentJoint = flowJointData[joint.parentIndex];
jointDebug.setDebugLine(joint.name, joint.node.currentPosition, !parentJoint ? MyAvatar.getJointPosition(joint.parentIndex) : parentJoint.node.currentPosition, {red: 255, green:(joint.colliding ? 0 : 255), blue:0}, joint.isDummy);
joint.apply();
}
}
self.computeThread(root);
}
var isActive, flowSkeleton, flowJointData, flowJointNameMap, flowThreads, handSystem, collisionSystem, collisionDebug, jointDebug;
function initFlow() {
stopFlow();
flowSkeleton = undefined;
flowJointData = [];
flowJointNameMap = {};
flowThreads = [];
handSystem = new FlowHandSystem();
collisionSystem = new FlowCollisionSystem();
collisionDebug = new FlowDebug();
jointDebug = new FlowDebug();
collisionDebug.setVisible(SHOW_DEBUG_SHAPES);
collisionDebug.setSolid(SHOW_SOLID_SHAPES);
MyAvatar.setEnableMeshVisible(SHOW_AVATAR);
jointDebug.setVisible(SHOW_DEBUG_SHAPES);
jointDebug.setSolid(SHOW_SOLID_SHAPES);
calculateConstraints();
isActive = true;
}
function stopFlow() {
isActive = false;
if (!flowSkeleton) {
return;
}
for (var i = 0; i < flowSkeleton.length; i++) {
var index = flowSkeleton[i].index;
MyAvatar.setJointRotation(index, flowJointData[index].initialXform.rot);
MyAvatar.setJointTranslation(index, flowJointData[index].initialXform.pos);
}
collisionDebug.cleanup();
jointDebug.cleanup();
}
function getUpHierarchyLevels(baseIndex) {
var upLevels = 0;
var currentIndex = baseIndex;
var siblings = [];
for (var i = 0; i < flowSkeleton.length; i++) {
var parentIndex = flowJointData[currentIndex].parentIndex;
if (flowJointData[parentIndex] != undefined) {
upLevels++;
currentIndex = parentIndex;
} else {
break;
}
}
return upLevels;
}
var calculateConstraints = function() {
var filterKeys = Object.keys(JOINT_FILTER_DATA);
flowSkeleton = MyAvatar.getSkeleton().filter(
function(jointInfo){
var name = jointInfo.name;
// We need a MyAvatar.getJointName(jointIndex) function
flowJointNameMap[name] = jointInfo.index;
var isFlowJoint = false;
var jointSettings = {};
var group = "";
for (var i = 0; i < filterKeys.length; i++) {
var jointGroup = JOINT_FILTER_DATA[filterKeys[i]];
var keyword = filterKeys[i].toUpperCase();
if (name.toUpperCase().indexOf(keyword) > -1) {
isFlowJoint = true;
jointSettings = jointGroup;
group = filterKeys[i];
break;
}
}
if (isFlowJoint){
if (flowJointData[jointInfo.index] === undefined) {
flowJointData[jointInfo.index] = new FlowJoint(jointInfo.index, jointInfo.parentIndex, name, group, jointSettings);
}
} else if (COLLISION_SHAPES[name]){
collisionSystem.addCollisionShape(JOINT_COLLISION_PREFIX + jointInfo.index, jointInfo.index, name, COLLISION_SHAPES[name]);
}
return isFlowJoint;
}
);
var roots = [];
for (var i = 0; i < flowSkeleton.length; i++) {
var index = flowSkeleton[i].index;
var flowJoint = flowJointData[index];
var parentFlowJoint = flowJointData[flowJoint.parentIndex];
if (parentFlowJoint != undefined) {
parentFlowJoint.childIndex = index;
} else {
flowJoint.node.anchored = true;
roots.push(index);
}
}
for (var i = 0; i < roots.length; i++) {
var thread = new FlowThread(roots[i]);
// add threads with at least 2 joints
if (thread.joints.length > 1) {
flowThreads.push(thread);
}
}
var jointCount = flowJointData.length;
var extraIndex = flowJointData.length;
var rightHandIndex = MyAvatar.getJointIndex("RightHand");
var rightHandPosition = MyAvatar.getJointPosition(rightHandIndex);
var parentIndex = rightHandIndex;
for (var i = 0; i < DUMMY_JOINT_COUNT; i++) {
var childIndex = (i == (DUMMY_JOINT_COUNT - 1)) ? -1 : extraIndex + 1;
flowJointData[extraIndex] = new FlowJointDummy(rightHandPosition, extraIndex, parentIndex, childIndex, JOINT_FILTER_DATA[DUMMY_GROUP_NAME]);
parentIndex = extraIndex;
extraIndex++;
}
flowJointData[jointCount].node.anchored = true;
var extraThread = new FlowThread(jointCount);
flowThreads.push(extraThread);
}
Script.update.connect(function(){
if (!isActive || !flowSkeleton) return;
varsToDebug.flowJoint = flowJointData[flowThreads[0].joints[flowThreads[0].joints.length-1]];
if (USE_COLLISIONS) {
Script.beginProfileRange("JS-Update-Collisions");
collisionSystem.update();
handSystem.update();
Script.endProfileRange("JS-Update-Collisions");
}
Script.beginProfileRange("JS-Update-JointsUpdate");
flowThreads.forEach(function(thread){
thread.update();
});
Script.endProfileRange("JS-Update-JointsUpdate");
Script.beginProfileRange("JS-Update-JointsSolve");
flowThreads.forEach(function(thread){
thread.solve(USE_COLLISIONS);
});
Script.endProfileRange("JS-Update-JointsSolve");
Script.beginProfileRange("JS-Update-JointsApply");
flowThreads.forEach(function(thread){
thread.apply();
});
Script.endProfileRange("JS-Update-JointsApply");
});
Script.scriptEnding.connect(stopFlow);
var varsToDebug = {
"flowJoint" : new FlowJoint(1,0,"test","Extra",JOINT_FILTER_DATA[JOINT_FILTER_KEYWORDS[0]]),
"init": function() {
initFlow();
},
"stop": function() {
stopFlow();
},
"isActive": function() {
return isActive;
},
"toggleAvatarVisible": function() {
SHOW_AVATAR = !SHOW_AVATAR;
MyAvatar.setEnableMeshVisible(SHOW_AVATAR);
},
"toggleDebugShapes": function() {
SHOW_DEBUG_SHAPES = !SHOW_DEBUG_SHAPES;
if (USE_COLLISIONS) {
collisionDebug.setVisible(SHOW_DEBUG_SHAPES);
}
jointDebug.setVisible(SHOW_DEBUG_SHAPES);
},
"toggleSolidShapes": function() {
SHOW_SOLID_SHAPES = !SHOW_SOLID_SHAPES;
collisionDebug.setSolid(SHOW_SOLID_SHAPES);
jointDebug.setSolid(SHOW_SOLID_SHAPES);
},
"toggleCollisions": function() {
USE_COLLISIONS = !USE_COLLISIONS;
if (USE_COLLISIONS && SHOW_DEBUG_SHAPES) {
collisionDebug.setVisible(true);
} else {
collisionDebug.setVisible(false);
}
},
"setFriction": function(friction) {
COLLISION_FRICTION = friction;
},
"setValue": function(group, name, value) {
if (group.indexOf("Joint : ") > -1) {
var jointName = group.split(" : ")[1];
var floatVal = parseFloat(value);
collisionSystem.modifyCollision(jointName, name, floatVal);
} else {
var keyword = group.toUpperCase();
for (var i = 0; i < flowThreads.length; i++) {
for (var j = 0; j < flowThreads[i].joints.length; j++){
var index = flowThreads[i].joints[j];
var joint = flowJointData[index];
if (joint.name.toUpperCase().indexOf(keyword) > -1) {
var floatVal = parseFloat(value);
JOINT_FILTER_DATA[group][name] = floatVal;
if (name === "stiffness") {
joint.stiffness = floatVal;
} else {
joint.node[name] = floatVal;
}
}
}
}
}
},
"getGroupData": function() {
var joint_filter_keys = JOINT_FILTER_DATA;
if (isActive) {
joint_filter_keys = {};
for (var i = 0; i < JOINT_FILTER_KEYWORDS.length; i++) {
joint_filter_keys[JOINT_FILTER_KEYWORDS[i]] = undefined;
}
for (var i = 0; i < flowThreads.length; i++) {
for (var j = 0; j < flowThreads[i].joints.length; j++){
var index = flowThreads[i].joints[j];
var joint = flowJointData[index];
if (!joint_filter_keys[joint.group]) {
joint_filter_keys[joint.group] = {
"stiffness": joint.stiffness,
"radius": joint.node.radius,
"gravity": joint.node.gravity,
"damping": joint.node.damping,
"inertia": joint.node.inertia,
"delta": joint.node.delta,
"friction": joint.node.friction
};
}
}
}
}
return joint_filter_keys;
},
"getSettingsData": function() {
return {"avatar": SHOW_AVATAR, "collisions": USE_COLLISIONS, "debug": SHOW_DEBUG_SHAPES, "solid": SHOW_SOLID_SHAPES};
},
"getCollisionData": function() {
if (isActive) {
return collisionSystem.getCollisionData();
} else {
var keys = Object.keys(COLLISION_SHAPES);
var data = [];
for (var i = 0; i < keys.length; i++) {
var row = COLLISION_SHAPES[keys[i]];
row["jointName"] = "Joint : "+keys[i];
data.push(row);
}
return data;
}
}
};
// Register GlobalDebugger for API Debugger
Script.registerValue("GlobalDebugger", varsToDebug);
// Capture the controller values
var leftTriggerPress = function (val) {
var value = (val <= 1) ? val : 0;
handSystem.setLeftTriggerValue(value);
};
var rightTriggerPress = function (val) {
var value = (val <= 1) ? val : 0;
handSystem.setRightTriggerValue(value);
};
var MAPPING_NAME = "com.highfidelity.hairTouch";
var mapping = Controller.newMapping(MAPPING_NAME);
mapping.from([Controller.Standard.RT]).peek().to(rightTriggerPress);
mapping.from([Controller.Standard.LT]).peek().to(leftTriggerPress);
Controller.enableMapping(MAPPING_NAME);
console.log("Hair Script loaded");
if (typeof FLOWAPP === 'undefined') {
initFlow();
}
}());