795 lines
32 KiB
JavaScript
795 lines
32 KiB
JavaScript
(function(){
|
|
|
|
var SHOW_DEBUG_SHAPES = false;
|
|
var USE_COLLISIONS = true;
|
|
|
|
var JOINT_FILTER_KEYWORDS = [
|
|
{"keyword": "hair", "stiffness": 0.0, "gravity": {x: 0, y: -0.0096, z: 0} ,"damping": 0.85, "inertia": 0.25, "delta": 0.45},
|
|
{"keyword": "skirt", "stiffness": 0.0, "gravity": {x: 0, y: -0.0096, z: 0} ,"damping": 0.85, "inertia": 0.25, "delta": 0.45},
|
|
{"keyword": "Breast", "stiffness": 1, "gravity": {x: 0, y: -0.0096, z: 0} ,"damping": 0.65, "inertia": 0.8, "delta": 0.45}
|
|
];
|
|
|
|
var CONTRAINT_ITERATIONS = 1;
|
|
var COLLISION_FRICTION = 0.0;
|
|
var COLLISION_REPULSION = 1;
|
|
|
|
var TOUCHING_DISTANCE = 2.0;
|
|
var JOINT_COLLISION_PREFIX = "joint_";
|
|
var HAND_COLLISION_PREFIX = "hand_";
|
|
var HAND_COLLISION_RADIUS = 0.08;
|
|
|
|
// 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 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);
|
|
}
|
|
|
|
var grabPower = {
|
|
"left": 0.0,
|
|
"right": 0.0
|
|
};
|
|
|
|
var COLLISION_SHAPES = {
|
|
"Head": {type: "sphere", radius: 0.12, offset: {x: 0, y: 0.02, z: 0.00}},
|
|
//"Spine2": {type: "cube", dimensions: {x: 0.35, y: 0.35, z: 0.08}, offset: {x: 0, y: -0.0, z: 0.01}},
|
|
//"LeftUpLeg": {type: "cube", dimensions: {x: 0.12, y: 0.45, z: 0.12}, offset: {x: 0, y: 0.25, z: 0.0}},
|
|
//"RightUpLeg": {type: "cube", dimensions: {x: 0.12, y: 0.45, z: 0.12}, offset: {x: 0, y: 0.25, z: 0.0}}
|
|
"LeftShoulder": {type: "sphere",radius: 0.09, offset: {x: 0, y: 0.05, z: 0.00}},
|
|
"RightShoulder": {type: "sphere",radius: 0.09, offset: {x: 0, y: 0.05, z: 0.00}},
|
|
"Spine2": {type: "sphere",radius: 0.09, offset: {x: 0, y: 0.05, z: 0.00}}
|
|
//"RightArm": {type: "sphere", radius: 0.07, offset: {x: 0, y: 0.00, z: 0.00}},
|
|
//"LeftArm": {type: "sphere", radius: 0.07, offset: {x: 0, y: 0.00, z: 0.00}}
|
|
//"Spine2": {radius: 0.09, offset: {x: 0, y: 0.06, z: 0.00}},
|
|
//"Spine1": {radius: 0.09, offset: {x: 0, y: 0.02, z: 0.00}}
|
|
};
|
|
|
|
var nearbyAvatarHands = {};
|
|
|
|
|
|
var FlowHandSystem = function() {
|
|
var self = this;
|
|
this.HANDS_COLLISION_JOINTS = ["RightHandMiddle1", "LeftHandMiddle1"];
|
|
this.avatarIds = [];
|
|
this.avatarHands = [];
|
|
this.lastAvatarCount = 0;
|
|
this.leftTriggerValue = 0;
|
|
this.rightTriggerValue = 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(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;
|
|
newHands[side] = new FlowCollisionSphere(name, jointId, HAND_COLLISION_RADIUS, {x: 0, y: 0, z: 0}, avatarID);
|
|
}
|
|
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.checkCollisions = function(point, index) {
|
|
var collision = new FlowCollisionData();
|
|
var avatarId, side;
|
|
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];
|
|
collision = self.avatarHands[i][side].checkCollision(point, index);
|
|
if (collision.offset > 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (collision.offset > 0) {
|
|
avatarId = self.avatarIds[i];
|
|
if (avatarId == MyAvatar.SELF_ID) {
|
|
var grabbing_coeficient = (side == self.HANDS_COLLISION_JOINTS[1]) ? self.leftTriggerValue : self.rightTriggerValue;
|
|
if (grabbing_coeficient > 0.1) {
|
|
varsToDebug.grabbing_coeficient = grabbing_coeficient;
|
|
collision.normal = addVec3s(collision.normal, scaleVec3(collision.normal, -2 * grabbing_coeficient));
|
|
collision.radius = collision.radius - (collision.radius * collision.offset);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return collision;
|
|
}
|
|
|
|
this.setRightTriggerValue = function(value) {
|
|
self.rightTriggerValue = value;
|
|
}
|
|
|
|
this.setLeftTriggerValue = function(value) {
|
|
self.leftTriggerValue = value;
|
|
}
|
|
}
|
|
|
|
var handSystem = new FlowHandSystem();
|
|
|
|
var FlowDebug = function() {
|
|
var self = this;
|
|
this.debugLines = {};
|
|
this.debugSpheres = {};
|
|
this.debugCubes = {};
|
|
this.showDebugShapes = false;
|
|
this.solidShapes = false;
|
|
|
|
this.setDebugCube = function(cubeName, cubePosition, cubeRotation, cubeDimensions, shapeColor) {
|
|
if (!self.showDebugShapes) 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.solidShapes,
|
|
visible: self.showDebugShapes
|
|
});
|
|
} else if (self.showDebugShapes) {
|
|
self.debugCubes[cubeName] = Overlays.addOverlay("cube", {
|
|
position: position,
|
|
rotation: rotation,
|
|
dimensions: dimensions,
|
|
color: color,
|
|
solid: self.solidShapes,
|
|
visible: self.showDebugShapes
|
|
});
|
|
}
|
|
}
|
|
|
|
this.setDebugLine = function(lineName, startPosition, endPosition, shapeColor) {
|
|
if (!self.showDebugShapes) 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: self.showDebugShapes
|
|
});
|
|
} else if (self.showDebugShapes) {
|
|
self.debugLines[lineName] = Overlays.addOverlay("line3d", {
|
|
color: color,
|
|
start: start,
|
|
end: end,
|
|
visible: self.showDebugShapes
|
|
});
|
|
}
|
|
}
|
|
|
|
this.setDebugSphere = function(sphereName, pos, diameter, shapeColor) {
|
|
if (!self.showDebugShapes) 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,
|
|
solid: self.solidShapes,
|
|
visible: self.showDebugShapes
|
|
});
|
|
} else if (self.showDebugShapes){
|
|
self.debugSpheres[sphereName] = Overlays.addOverlay("sphere", {
|
|
color: color,
|
|
position: pos,
|
|
scale: {x:scale, y:scale, z:scale},
|
|
solid: self.solidShapes,
|
|
visible: self.showDebugShapes
|
|
});
|
|
}
|
|
}
|
|
|
|
this.deleteSphere = function(name) {
|
|
Overlays.deleteOverlay(flowDebug.debugSpheres[name]);
|
|
}
|
|
|
|
this.deleteLine = function(name) {
|
|
Overlays.deleteOverlay(flowDebug.debugLines[name]);
|
|
}
|
|
|
|
this.deleteCube = function(name) {
|
|
Overlays.deleteOverlay(flowDebug.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);
|
|
}
|
|
}
|
|
|
|
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.toggleVisible = function() {
|
|
self.showDebugShapes = !self.showDebugShapes;
|
|
}
|
|
}
|
|
|
|
var flowDebug = new FlowDebug();
|
|
flowDebug.setVisible(SHOW_DEBUG_SHAPES);
|
|
|
|
var FlowCollisionSystem = function() {
|
|
var self = this;
|
|
this.collisionSpheres = [];
|
|
this.collisionCubes = [];
|
|
this.addCollisionSphere = function(name, jointIndex, radius, offset) {
|
|
self.collisionSpheres.push(new FlowCollisionSphere(name, jointIndex, radius, offset));
|
|
}
|
|
this.addCollisionCube = function(name, jointIndex, dimensions, offset) {
|
|
self.collisionCubes.push(new FlowCollisionCube(name, jointIndex, dimensions, offset));
|
|
}
|
|
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, collisions[i].normal);
|
|
collisionData.position = addVec3s(collisionData.position, collisions[i].position);
|
|
collisionData.radius += collisions[i].radius
|
|
}
|
|
collisionData.offset = collisionData.offset/collisions.length;
|
|
collisionData.normal = normalizeVec3(scaleVec3(collisionData.normal, 1/collisions.length));
|
|
collisionData.position = scaleVec3(collisionData.position, 1/collisions.length);
|
|
collisionData.radius = dotVec3s(scaleVec3(collisions[0].normal, collisions[0].radius), collisionData.normal);
|
|
} else if (collisions.length == 1){
|
|
collisionData = collisions[0];
|
|
}
|
|
collisionData.collisionCount = collisions.length;
|
|
return collisionData;
|
|
}
|
|
|
|
this.checkCollisions = function(point, index) {
|
|
var collisions = [];
|
|
for (var i = 0; i < self.collisionSpheres.length; i++) {
|
|
var collision = self.collisionSpheres[i].checkCollision(point, index);
|
|
if (collision.offset > 0) {
|
|
collisions.push(collision);
|
|
}
|
|
}
|
|
for (var i = 0; i < self.collisionCubes.length; i++) {
|
|
var collision = self.collisionCubes[i].checkCollision(point, index);
|
|
console.log("checking");
|
|
if (collision.offset > 0) {
|
|
console.log("collision");
|
|
collisions.push(collision);
|
|
}
|
|
}
|
|
return self.computeCollision(collisions);
|
|
}
|
|
}
|
|
|
|
var FlowCollisionData = function(offset, position, radius, normal) {
|
|
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};
|
|
}
|
|
|
|
var FlowCollisionSphere = function(name, jointIndex, radius, offset, avatarId) {
|
|
var self = this;
|
|
this.name = name;
|
|
this.radius = radius;
|
|
this.jointIndex = jointIndex;
|
|
this.position = {x:0, y:0, z:0};
|
|
this.offset = offset;
|
|
this.avatarId = avatarId;
|
|
|
|
this.update = function() {
|
|
if (self.avatarId != undefined){
|
|
var avatar = AvatarList.getAvatar(self.avatarId);
|
|
self.position = avatar.getJointPosition(self.jointIndex);
|
|
} else {
|
|
self.position = MyAvatar.jointToWorldPoint(offset, jointIndex);
|
|
}
|
|
flowDebug.setDebugSphere(self.name, self.position, 2*self.radius, {red: 200, green: 10, blue: 50});
|
|
}
|
|
|
|
this.checkCollision = function(point, index) {
|
|
var centerToJoint = subtractVec3s(point, self.position);
|
|
var distance = lengthVec3(centerToJoint);
|
|
var offset = self.radius - distance;
|
|
var collisionData = new FlowCollisionData(offset, self.position, self.radius, normalizeVec3(centerToJoint));
|
|
return collisionData;
|
|
}
|
|
}
|
|
|
|
var FlowCollisionCube = function(name, jointIndex, dimensions, offset, avatarId) {
|
|
var self = this;
|
|
this.name = name;
|
|
this.dimensions = dimensions;
|
|
this.jointIndex = jointIndex;
|
|
this.position = {x:0, y:0, z:0};
|
|
this.rotation = {x:0, y:0, z:0, w:0};
|
|
this.offset = offset;
|
|
this.avatarId = avatarId;
|
|
|
|
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);
|
|
}
|
|
flowDebug.setDebugCube(self.name, self.position, self.rotation, self.dimensions, {red: 200, green: 10, blue: 50});
|
|
}
|
|
|
|
this.checkCollision = function(point, index) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
var FlowAvatar = function() {
|
|
var self = this;
|
|
this.lastPosition = MyAvatar.position;
|
|
this.currentPosition = MyAvatar.position;
|
|
this.lastVelocity = {x: 0, y: 0, z:0};
|
|
this.currentVelocity = {x: 0, y: 0, z:0};
|
|
this.acceleration = {x: 0, y: 0, z:0};
|
|
this.update = function() {
|
|
self.currentVelocity = subtractVec3s(self.currentPosition, self.lastPosition);
|
|
self.acceleration = subtractVec3s(self.currentVelocity, self.lastVelocity);
|
|
self.lastPosition = self.currentPosition;
|
|
self.lastVelocity = self.currentVelocity;
|
|
}
|
|
}
|
|
|
|
var FlowJoint = function(index, parentIndex, name, config){
|
|
var self = this;
|
|
|
|
this.index = index;
|
|
this.name = name;
|
|
this.parentIndex = parentIndex;
|
|
this.childIndex = -1;
|
|
this.stiffness = config.stiffness;
|
|
this.gravity = config.gravity;
|
|
this.damping = config.damping;
|
|
this.inertia = config.inertia;
|
|
this.delta = config.delta;
|
|
|
|
this.initialPosition = MyAvatar.getJointPosition(index);
|
|
this.initialRotation = MyAvatar.getJointRotation(index);
|
|
this.initialTranslation = MyAvatar.getJointTranslation(index);
|
|
|
|
this.previousPosition = this.initialPosition;
|
|
this.currentPosition = this.initialPosition;
|
|
this.translationDirection = normalizeVec3(this.initialTranslation);
|
|
|
|
this.length = lengthVec3(subtractVec3s(this.initialPosition, MyAvatar.getJointPosition(self.parentIndex)));
|
|
|
|
this.velocity = {x:0, y:0, z:0};
|
|
this.acceleration = {x:0, y:0, z:0};
|
|
this.recovery = {x:0, y:0, z:0};
|
|
this.parents = [];
|
|
this.ajusted = false;
|
|
this.anchored = false;
|
|
this.colliding = false;
|
|
this.collision;
|
|
|
|
this.computeLookAtRotation = function(jointIndex, parentIndex, point) {
|
|
var jointPosition = MyAvatar.getJointPosition(jointIndex);
|
|
var jointRotation = MyAvatar.getJointRotation(jointIndex);
|
|
var localJoint = MyAvatar.worldToJointPoint(jointPosition, parentIndex);
|
|
var localPoint = MyAvatar.worldToJointPoint(point, parentIndex);
|
|
|
|
var localUp = MyAvatar.worldToJointPoint(Vec3.sum(MyAvatar.getJointPosition(parentIndex), {x: 0, y: 1, z: 0}), parentIndex);
|
|
var distance = Vec3.distance(jointPosition, point);
|
|
var lookAtRotation = Quat.lookAt(localPoint, localJoint, localUp);
|
|
|
|
var corrector = Quat.fromVec3Degrees({x: 90, y: 0, z: 0});
|
|
var jointRotation = Quat.multiply(lookAtRotation, corrector);
|
|
return {rotation: jointRotation, distance: distance};
|
|
}
|
|
|
|
this.getJointRotationToPoint2 = function(jointIndex, parentIndex, grandParentIndex, point) {
|
|
var jointPosition = MyAvatar.getJointPosition(jointIndex);
|
|
var parentPosition = MyAvatar.getJointPosition(parentIndex);
|
|
var parentRotation = MyAvatar.getJointRotation(parentIndex);
|
|
// local frame in grandparent since parent rotation is also grandparent frame
|
|
var localJointPosition = MyAvatar.worldToJointPoint(jointPosition, grandParentIndex);
|
|
var localParentPosition = MyAvatar.worldToJointPoint(parentPosition, grandParentIndex);
|
|
var localPointPosition = MyAvatar.worldToJointPoint(point, grandParentIndex);
|
|
var parentToJoint = Vec3.subtract(localJointPosition, localParentPosition);
|
|
var parentToPoint = Vec3.subtract(localPointPosition, localParentPosition);
|
|
var deltaRotation = Quat.rotationBetween(parentToJoint, parentToPoint);
|
|
var jointRotation = Quat.multiply(deltaRotation, parentRotation);
|
|
var distance = Vec3.distance(localParentPosition, localPointPosition);
|
|
return {rotation: jointRotation, distance: distance};
|
|
}
|
|
|
|
this.getJointRotationToPoint = function(jointIndex, parentIndex, grandParentIndex, point) {
|
|
var parentRotation = MyAvatar.getJointRotation(parentIndex);
|
|
var translation = Vec3.multiply(MyAvatar.worldToJointPoint(point, parentIndex), 100);
|
|
return {rotation: parentRotation, translation: translation};
|
|
}
|
|
|
|
this.setJointPosition = function(jointIndex, parentIndex, grandParentIndex, position) {
|
|
//var parentRotation = MyAvatar.getJointRotation(parentIndex);
|
|
var translation = scaleVec3(MyAvatar.worldToJointPoint(position, parentIndex), 100);
|
|
//MyAvatar.setJointRotation(parentIndex, parentRotation);
|
|
MyAvatar.setJointTranslation(jointIndex, translation);
|
|
}
|
|
|
|
this.update = function () {
|
|
self.acceleration = self.gravity;
|
|
self.velocity = subtractVec3s(self.currentPosition, self.previousPosition);
|
|
self.previousPosition = self.currentPosition;
|
|
if (!self.anchored) {
|
|
var recoveryPosition = MyAvatar.jointToWorldPoint(scaleVec3(self.initialTranslation, 0.01), parentIndex);
|
|
self.recovery = subtractVec3s(recoveryPosition, self.currentPosition);
|
|
self.acceleration = addVec3s(self.acceleration, scaleVec3(self.velocity, self.inertia));
|
|
self.acceleration = addVec3s(self.acceleration, scaleVec3(self.recovery, self.stiffness));
|
|
self.currentPosition = addVec3s(addVec3s(self.currentPosition, scaleVec3(self.velocity, self.damping)), scaleVec3(self.acceleration, Math.pow(self.delta, 2)));
|
|
|
|
} else {
|
|
self.acceleration = {x:0, y:0, z:0};
|
|
self.velocity = {x:0, y:0, z:0};
|
|
self.currentPosition = MyAvatar.getJointPosition(self.index);
|
|
}
|
|
}
|
|
|
|
this.ajust = function() {
|
|
var parentJoint = flowJointData[self.parentIndex];
|
|
if (!parentJoint) {
|
|
return;
|
|
}
|
|
var worldVelocity = subtractVec3s(MyAvatar.jointToWorldPoint(parentJoint.velocity, parentJoint.index), MyAvatar.getJointPosition(parentJoint.index));
|
|
var worldVelocity = addVec3s(MyAvatar.getJointPosition(self.index), worldVelocity);
|
|
var jointVelocity = MyAvatar.worldToJointPoint(worldVelocity, self.index);
|
|
self.velocity = subtractVec3s(self.velocity, jointVelocity);
|
|
}
|
|
|
|
this.solve = function(collision) {
|
|
var parentPosition = flowJointData[self.parentIndex] ? flowJointData[self.parentIndex].currentPosition : MyAvatar.getJointPosition(self.parentIndex);
|
|
var parentToJoint = subtractVec3s(self.currentPosition, parentPosition);
|
|
var difference = self.length/lengthVec3(parentToJoint);
|
|
self.currentPosition = difference < 1.0 ? addVec3s(parentPosition, scaleVec3(parentToJoint, difference)) : self.currentPosition;
|
|
|
|
self.colliding = collision && (collision.offset > 0);
|
|
self.collision = collision;
|
|
|
|
if (self.colliding) {
|
|
var normal = normalizeVec3(subtractVec3s(self.currentPosition, self.collision.position));
|
|
var position = addVec3s(self.collision.position, scaleVec3(normal, self.collision.radius));
|
|
self.currentPosition = addVec3s(scaleVec3(position, (1 - COLLISION_FRICTION)), scaleVec3(self.previousPosition, COLLISION_FRICTION));
|
|
}
|
|
|
|
}
|
|
|
|
this.apply = function() {
|
|
var parentJoint;
|
|
if (!self.anchored) {
|
|
parentJoint = flowJointData[self.parentIndex];
|
|
if (!parentJoint) {
|
|
return;
|
|
}
|
|
var grandpaJointIndex = parentJoint.parentIndex;
|
|
self.setJointPosition(self.index, parentJoint.index, grandpaJointIndex, self.currentPosition);
|
|
}
|
|
flowDebug.setDebugSphere(self.name, self.currentPosition, 0.02, {red: self.collision.collisionCount > 1 ? 0 : 255, green:self.colliding ? 0 : 255, blue:0});
|
|
flowDebug.setDebugLine(self.name, self.currentPosition, !parentJoint ? MyAvatar.getJointPosition(self.parentIndex) : parentJoint.currentPosition, {red: 255, green:(self.colliding ? 0 : 255), blue:0});
|
|
}
|
|
}
|
|
|
|
var flowSkeleton;
|
|
var flowJointData = [];
|
|
var flowJointNameMap = {};
|
|
var flowThreads = [];
|
|
var flowAvatar = new FlowAvatar();
|
|
var updateOrder = [];
|
|
var collisionSystem = new FlowCollisionSystem();
|
|
|
|
|
|
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() {
|
|
|
|
flowJointData = [];
|
|
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 config;
|
|
for (var i = 0; i < JOINT_FILTER_KEYWORDS.length; i++) {
|
|
if (name.indexOf(JOINT_FILTER_KEYWORDS[i].keyword) > -1) {
|
|
isFlowJoint = true;
|
|
config = JOINT_FILTER_KEYWORDS[i];
|
|
break;
|
|
}
|
|
}
|
|
if (isFlowJoint){
|
|
if (flowJointData[jointInfo.index] === undefined) {
|
|
flowJointData[jointInfo.index] = new FlowJoint(jointInfo.index, jointInfo.parentIndex, name, config);
|
|
}
|
|
} else if (COLLISION_SHAPES[name]){
|
|
switch(COLLISION_SHAPES[name].type) {
|
|
case "sphere":
|
|
collisionSystem.addCollisionSphere(JOINT_COLLISION_PREFIX + jointInfo.index, jointInfo.index, COLLISION_SHAPES[name].radius, COLLISION_SHAPES[name].offset);
|
|
break;
|
|
case "cube":
|
|
collisionSystem.addCollisionCube(JOINT_COLLISION_PREFIX + jointInfo.index, jointInfo.index, COLLISION_SHAPES[name].dimensions, COLLISION_SHAPES[name].offset);
|
|
break;
|
|
}
|
|
}
|
|
return isFlowJoint;
|
|
}
|
|
);
|
|
|
|
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.anchored = true;
|
|
}
|
|
|
|
}
|
|
|
|
for (var i = 0; i < flowSkeleton.length; i++) {
|
|
var index = flowSkeleton[i].index;
|
|
var flowJoint = flowJointData[index];
|
|
if (flowJoint.anchored && flowJoint.childIndex === -1) {
|
|
flowJoint.anchored = false;
|
|
}
|
|
updateOrder.push({"index": index, "upLevels": getUpHierarchyLevels(index)});
|
|
}
|
|
|
|
updateOrder.sort(function(a, b) {
|
|
return a.upLevels - b.upLevels;
|
|
});
|
|
}
|
|
|
|
calculateConstraints();
|
|
|
|
Script.update.connect(function(){
|
|
if (!flowSkeleton) return;
|
|
|
|
if (USE_COLLISIONS) {
|
|
Script.beginProfileRange("JS-Update-Collisions");
|
|
collisionSystem.update();
|
|
handSystem.update();
|
|
Script.endProfileRange("JS-Update-Collisions");
|
|
}
|
|
|
|
Script.beginProfileRange("JS-Update-JointsUpdate");
|
|
|
|
for (var i = 0; i < updateOrder.length; i++) {
|
|
var index = updateOrder[i].index;
|
|
var joint = flowJointData[index];
|
|
joint.update();
|
|
}
|
|
Script.endProfileRange("JS-Update-JointsUpdate");
|
|
Script.beginProfileRange("JS-Update-JointsSolve");
|
|
for (var j = 0; j < CONTRAINT_ITERATIONS; j++) {
|
|
for (var i = 0; i < updateOrder.length; i++) {
|
|
var index = updateOrder[i].index;
|
|
var joint = flowJointData[index];
|
|
var jointPosition = MyAvatar.getJointPosition(joint.index);
|
|
var bodyCollision = USE_COLLISIONS ? collisionSystem.checkCollisions(jointPosition, joint.index) : undefined;
|
|
var handCollision = USE_COLLISIONS ? handSystem.checkCollisions(jointPosition, joint.index) : undefined;
|
|
var collision = (handCollision && handCollision.offset > 0) ? handCollision : bodyCollision;
|
|
flowJointData[index].solve(collision);
|
|
}
|
|
}
|
|
Script.endProfileRange("JS-Update-JointsSolve");
|
|
Script.beginProfileRange("JS-Update-JointsApply");
|
|
for (var i = 0; i < updateOrder.length; i++) {
|
|
var index = updateOrder[i].index;
|
|
flowJointData[index].apply();
|
|
}
|
|
Script.endProfileRange("JS-Update-JointsApply");
|
|
});
|
|
|
|
Script.scriptEnding.connect(function () {
|
|
for (var i = 0; i < flowSkeleton.length; i++) {
|
|
var index = flowSkeleton[i].index;
|
|
MyAvatar.setJointRotation(index, flowJointData[index].initialRotation);
|
|
MyAvatar.setJointTranslation(index, flowJointData[index].initialTranslation);
|
|
}
|
|
flowDebug.cleanup();
|
|
});
|
|
|
|
var varsToDebug = {
|
|
"toggleDebugShapes": function() {
|
|
flowDebug.setVisible(!flowDebug.showDebugShapes);
|
|
},
|
|
"toggleCollisions": function() {
|
|
USE_COLLISIONS = !USE_COLLISIONS;
|
|
},
|
|
"setFriction": function(friction) {
|
|
COLLISION_FRICTION = friction;
|
|
},
|
|
"setRepulsion": function(repulsion) {
|
|
COLLISION_REPULSION = repulsion;
|
|
},
|
|
"recovery": {x:0, y:0, z:0},
|
|
"grabbing_coeficient": 0
|
|
};
|
|
|
|
// Register GlobalDebugger for API Debugger
|
|
Script.registerValue("GlobalDebugger", varsToDebug);
|
|
|
|
|
|
// Capture the controller values
|
|
|
|
var leftTriggerPress = function (val) {
|
|
var value = (val <= 1) ? val : 0;
|
|
grabPower.left = value;
|
|
handSystem.setLeftTriggerValue(value);
|
|
};
|
|
|
|
var rightTriggerPress = function (val) {
|
|
var value = (val <= 1) ? val : 0;
|
|
grabPower.right = value;
|
|
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);
|
|
|
|
})()
|