content/hifi-content/ben/flow2.js
2022-02-13 21:50:01 +01:00

1482 lines
No EOL
62 KiB
JavaScript

//
// Created by Luis Cuenca on 1/31/18
// Copyright 2018 High Fidelity, Inc.
//
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* jslint bitwise: true */
/* global Script, Overlays, Controller, MyAvatar, QUAT, VEC3, AvatarList, Xform
*/
Script.include("/~/system/libraries/Xform.js");
Script.include(Script.resolvePath("./VectorMath.js"));
(function(){
var SHOW_AVATAR = true;
var SHOW_DEBUG_SHAPES = false;
var SHOW_SOLID_SHAPES = false;
var SHOW_DUMMY_JOINTS = false;
var USE_COLLISIONS = true;
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 = 40;
var FLOW_JOINT_PREFIX = "flow";
var SIM_JOINT_PREFIX = "sim";
var JOINT_COLLISION_PREFIX = "joint_";
var HAND_COLLISION_PREFIX = "hand_";
var HAND_COLLISION_RADIUS = 0.03;
var HAND_TOUCHING_DISTANCE = 2.0;
var COLLISION_SHAPES_LIMIT = 4;
var DUMMY_KEYWORD = "Extra";
var DUMMY_JOINT_COUNT = 8;
var DUMMY_JOINT_DISTANCE = 0.05;
var ISOLATED_JOINT_STIFFNESS = 0.85;
var ISOLATED_JOINT_LENGTH = 0.05;
// Joint groups by keyword
var FLOW_JOINT_KEYWORDS = [];
var FLOW_JOINT_DATA = {};
var DEFAULT_JOINT_SETTINGS = { "get": function() {
return {"active": true, "stiffness": 0.0, "gravity": -0.0096 ,"damping": 0.85, "inertia": 0.8, "delta": 0.55, "radius": 0.01};
}};
var DEFAULT_COLLISION_SETTINGS = {type: "sphere", radius: 0.05, offset: {x: 0, y: 0, z: 0}};
var PRESET_FLOW_DATA = {
"hair": {"active": true, "stiffness": 0.0, "gravity": -0.0096 ,"damping": 0.85, "inertia": 0.8, "delta": 0.55, "radius": 0.01},
"skirt": {"active": true, "stiffness": 0.0, "gravity": -0.0096 ,"damping": 0.85, "inertia": 0.25, "delta": 0.45, "radius": 0.01},
"breasts": {"active": true, "stiffness": 1, "gravity": -0.0096 ,"damping": 0.65, "inertia": 0.8, "delta": 0.45, "radius": 0.01}
};
var PRESET_COLLISION_DATA = {
"Head": {type: "sphere", radius: 0.08, offset: {x: 0, y: 0.06, 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.09, offset: {x: 0, y: 0.04, z: 0.00}}
};
var CUSTOM_FLOW_DATA, CUSTOM_COLLISION_DATA;
// CUSTOM DATA STARTS HERE
CUSTOM_FLOW_DATA = {
"tail": {
"active": true,
"stiffness": 0,
"radius": 0.01,
"gravity": -0.0096,
"damping": 0.85,
"inertia": 0.8,
"delta": 0.55
},
"cape": {
"active": true,
"stiffness": 0,
"radius": 0.01,
"gravity": -0.0096,
"damping": 0.85,
"inertia": 0.8,
"delta": 0.55
},
"breast": {
"active": true,
"stiffness": 0,
"radius": 0.01,
"gravity": -0.0096,
"damping": 0.85,
"inertia": 0.8,
"delta": 0.55
},
"ear": {
"active": true,
"stiffness": 0,
"radius": 0.01,
"gravity": -0.0096,
"damping": 0.85,
"inertia": 0.8,
"delta": 0.55
}
};
CUSTOM_COLLISION_DATA = {
"Spine2": {
"type": "sphere",
"radius": 0.14,
"offset": {
"x": 0,
"y": 0.2,
"z": 0
}
},
"RightArm": {
"type": "sphere",
"radius": 0.03,
"offset": {
"x": 0,
"y": 0.02,
"z": 0
}
},
"LeftArm": {
"type": "sphere",
"radius": 0.03,
"offset": {
"x": 0,
"y": 0.02,
"z": 0
}
}
};
// CUSTOM DATA ENDS HERE
var HAND_COLLISION_JOINTS = ["RightHandMiddle1", "RightHandThumb3", "LeftHandMiddle1", "LeftHandThumb3","RightHandMiddle3", "LeftHandMiddle3"];
if (SHOW_DUMMY_JOINTS) {
FLOW_JOINT_KEYWORDS.push(DUMMY_KEYWORD);
FLOW_JOINT_DATA[DUMMY_KEYWORD] = DEFAULT_JOINT_SETTINGS.get();
}
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 !== undefined ? 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]);
self.debugSpheres[name] = undefined;
};
this.deleteLine = function(name) {
Overlays.deleteOverlay(self.debugLines[name]);
self.debugLines[name] = undefined;
};
this.deleteCube = function(name) {
Overlays.deleteOverlay(self.debugCubes[name]);
self.debugCubes[name] = undefined;
};
this.cleanup = function() {
for (var lineName in self.debugLines) {
if (lineName !== undefined) {
self.deleteLine(lineName);
}
}
for (var sphereName in self.debugSpheres) {
if (sphereName !== undefined) {
self.deleteSphere(sphereName);
}
}
for (var cubeName in self.debugCubes) {
if (cubeName!== undefined) {
self.deleteCube(cubeName);
}
}
self.debugLines = {};
self.debugSpheres = {};
self.debugCubes = {};
};
this.setVisible = function(isVisible) {
self.showDebugShapes = isVisible;
for (var lineName in self.debugLines) {
if (lineName !== undefined) {
Overlays.editOverlay(self.debugLines[lineName], {
visible: isVisible
});
}
}
for (var sphereName in self.debugSpheres) {
if (sphereName !== undefined) {
Overlays.editOverlay(self.debugSpheres[sphereName], {
visible: isVisible
});
}
}
};
this.setSolid = function(isSolid) {
self.showSolidShapes = isSolid;
for (var lineName in self.debugLines) {
if (lineName !== undefined) {
Overlays.editOverlay(self.debugLines[lineName], {
solid: isSolid
});
}
}
for (var sphereName in self.debugSpheres) {
if (sphereName !== undefined) {
Overlays.editOverlay(self.debugSpheres[sphereName], {
solid: isSolid
});
}
}
};
};
var FlowHandSystem = function() {
var self = this;
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 VEC3.distance(avatarPosition, MyAvatar.position) < distance;
});
};
this.setScale = function(scale) {
for (var j = 0; j < self.avatarHands.length; j++) {
for (var i = 0; i < HAND_COLLISION_JOINTS.length; i++) {
var side = HAND_COLLISION_JOINTS[i];
self.avatarHands[j][side].radius = self.avatarHands[j][side].initialRadius * scale;
}
}
};
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);
var i, side;
if (avatarIndex === -1) {
var newHands = {};
for (i = 0; i < HAND_COLLISION_JOINTS.length; i++) {
side = HAND_COLLISION_JOINTS[i];
var jointId = avatar.getJointIndex(side);
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 (i = 0; i < HAND_COLLISION_JOINTS.length; i++) {
side = HAND_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 (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 = VEC3.sum(collisionData.normal, VEC3.multiply(collisions[i].normal, collisions[i].distance));
collisionData.position = VEC3.sum(collisionData.position, collisions[i].position);
collisionData.radius += collisions[i].radius;
collisionData.distance += collisions[i].distance;
}
collisionData.offset = collisionData.offset/collisions.length;
collisionData.radius = VEC3.length(collisionData.normal)/2;
collisionData.normal = VEC3.normalize(collisionData.normal);
collisionData.position = VEC3.multiply(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 (i = 0; i < self.avatarHands.length; i++) {
for (var j = 0; j < HAND_COLLISION_JOINTS.length; j++) {
var side = HAND_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);
var 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 (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(jointIndex, jointName, settings) {
var name = JOINT_COLLISION_PREFIX + jointIndex;
switch(settings.type) {
case "sphere":
self.addCollisionSphere(name, jointIndex, jointName, settings);
break;
case "cube":
self.addCollisionCube(name, jointIndex, jointName, settings);
break;
}
};
this.addCollisionToJoint = function(jointName) {
if (self.collisionSpheres.length >= COLLISION_SHAPES_LIMIT) {
return false;
}
var jointIndex = MyAvatar.getJointIndex(jointName);
var collisionIndex = self.findCollisionWithJoint(jointIndex);
if (collisionIndex === -1) {
self.addCollisionShape(jointIndex, jointName, DEFAULT_COLLISION_SETTINGS);
return true;
} else {
return false;
}
};
this.removeCollisionFromJoint = function(jointName) {
var jointIndex = MyAvatar.getJointIndex(jointName);
var collisionIndex = self.findCollisionWithJoint(jointIndex);
if (collisionIndex > -1) {
self.collisionSpheres[collisionIndex].clean();
self.collisionSpheres.splice(collisionIndex, 1);
}
};
this.update = function() {
for (var i = 0; i < self.collisionCubes.length; i++) {
self.collisionCubes[i].update();
}
for (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 = VEC3.sum(collisionData.normal, VEC3.multiply(collisions[i].normal, collisions[i].distance));
collisionData.position = VEC3.sum(collisionData.position, collisions[i].position);
collisionData.radius += collisions[i].radius;
collisionData.distance += collisions[i].distance;
}
collisionData.offset = collisionData.offset/collisions.length;
collisionData.radius = VEC3.length(collisionData.normal)/2;
collisionData.normal = VEC3.normalize(collisionData.normal);
collisionData.position = VEC3.multiply(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.setScale = function(scale) {
for (var j = 0; j < self.collisionSpheres.length; j++) {
self.collisionSpheres[j].radius = self.collisionSpheres[j].initialRadius * scale;
self.collisionSpheres[j].offset = VEC3.multiply(self.collisionSpheres[j].initialOffset, scale);
}
};
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);
var nextCollision;
if (!tooFar) {
if (checkSegments) {
for (i = 1; i < thread.joints.length; i++) {
var prevCollision = collisionData[i-1];
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 (i = 1; i < thread.joints.length; i++) {
nextCollision = self.collisionSpheres[j].checkCollision(thread.positions[i], thread.radius);
if (nextCollision.offset > 0) {
threadCollisionData[i].push(nextCollision);
}
}
}
}
}
var collisionResult = [];
for (i = 0; i < thread.joints.length; i++) {
collisionResult.push(self.computeCollision(threadCollisionData[i]));
}
return collisionResult;
};
this.findCollisionWithJoint = function (jointIndex) {
for (var i = 0; i < self.collisionSpheres.length; i++) {
if (self.collisionSpheres[i].jointIndex == jointIndex) {
return i;
}
}
return -1;
};
this.modifyCollision = function(jointName, parameter, value) {
var jointIndex = MyAvatar.getJointIndex(jointName);
var collisionIndex = self.findCollisionWithJoint(jointIndex);
var avatarScale = MyAvatar.scale;
if (collisionIndex > -1) {
switch(parameter) {
case "radius": {
self.collisionSpheres[collisionIndex].initialRadius = value;
self.collisionSpheres[collisionIndex].radius = avatarScale*value;
break;
}
case "offset": {
var offset = self.collisionSpheres[collisionIndex].offset;
self.collisionSpheres[collisionIndex].initialOffset = {x: offset.x, y: value, z: offset.z};
self.collisionSpheres[collisionIndex].offset = VEC3.multiply(self.collisionSpheres[collisionIndex].initialOffset, avatarScale);
break;
}
}
}
};
this.getCollisionData = function() {
var collisionData = {};
for (var i = 0; i < self.collisionSpheres.length; i++) {
var data = {type: "sphere", radius: self.collisionSpheres[i].initialRadius, offset: self.collisionSpheres[i].initialOffset};
collisionData[self.collisionSpheres[i].jointName] = 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 = this.initialRadius = settings.radius;
this.offset = settings.offset;
this.initialOffset = {x: self.offset.x, y: self.offset.y, z: self.offset.z};
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 = VEC3.subtract(point, self.position);
var distance = VEC3.length(centerToJoint) - radius;
var offset = self.radius - distance;
var collisionData = new FlowCollisionData(offset, self.position, self.radius, VEC3.normalize(centerToJoint), distance);
return collisionData;
};
this.checkSegmentCollision = function(point1, point2, pointCollision1, pointCollision2) {
var collisionData = new FlowCollisionData();
var segment = VEC3.subtract(point2, point1);
var segmentLength = VEC3.length(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 = VEC3.sum(point1, VEC3.multiply(segment, segmentPercent));
var centerToSegment = VEC3.subtract(collisionPoint, self.position);
var distance = VEC3.length(centerToSegment);
if (distance < self.radius) {
var offset = self.radius - distance;
collisionData = new FlowCollisionData(offset, self.position, self.radius, VEC3.normalize(centerToSegment), distance);
}
}
return collisionData;
};
this.clean = function() {
collisionDebug.deleteSphere(self.name);
};
};
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);
}
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 = VEC3.subtract(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)};
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 = VEC3.sum(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 = VEC3.sum(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 = VEC3.sum(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.active = settings.active;
this.radius = this.initialRadius = settings.radius;
this.gravity = settings.gravity;
this.damping = settings.damping;
this.inertia = settings.inertia;
this.delta = settings.delta;
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 = undefined;
this.update = function(accelerationOffset) {
self.acceleration = {x: 0, y: self.gravity, z: 0};
self.previousVelocity = self.currentVelocity;
self.currentVelocity = VEC3.subtract(self.currentPosition, self.previousPosition);
self.previousPosition = self.currentPosition;
if (!self.anchored) {
// Add inertia
var centrifugeVector = VEC3.normalize(VEC3.subtract(self.previousVelocity, self.currentVelocity));
self.acceleration = VEC3.sum(self.acceleration, VEC3.multiply(centrifugeVector, self.inertia * VEC3.length(self.currentVelocity)));
// Add offset
self.acceleration = VEC3.sum(self.acceleration, accelerationOffset);
// Calculate new position
self.currentPosition = VEC3.sum(
VEC3.sum(self.currentPosition, VEC3.multiply(self.currentVelocity, self.damping)),
VEC3.multiply(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) {
self.solveConstraints(constrainPoint, maxDistance);
self.solveCollisions(collision);
};
this.solveConstraints = function(constrainPoint, maxDistance) {
var constrainVector = VEC3.subtract(self.currentPosition, constrainPoint);
var difference = maxDistance/VEC3.length(constrainVector);
self.currentPosition = difference < 1.0 ? VEC3.sum(constrainPoint, VEC3.multiply(constrainVector, difference)) : self.currentPosition;
};
this.solveCollisions = function(collision) {
self.colliding = collision && (collision.offset > 0);
self.collision = collision;
if (self.colliding) {
self.currentPosition = VEC3.sum(self.currentPosition, VEC3.multiply(collision.normal, collision.offset));
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 = undefined;
this.recoveryPosition = undefined;
this.node = new FlowNode(self.initialPosition, settings);
this.stiffness = settings.stiffness;
this.translationDirection = VEC3.normalize(this.initialXform.pos);
this.length = VEC3.length(VEC3.subtract(this.initialPosition, MyAvatar.getJointPosition(self.parentIndex)));
this.update = function () {
var accelerationOffset = {x: 0, y: 0, z: 0};
if (self.recoveryPosition) {
var recoveryVector = VEC3.subtract(self.recoveryPosition, self.node.currentPosition);
accelerationOffset = VEC3.multiply(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() {
if (self.currentRotation) {
MyAvatar.setJointRotation(self.index, self.currentRotation);
}
self.node.apply(self.name, self.isDummy);
};
};
var FlowJointDummy = function(initialPosition, index, parentIndex, childIndex, settings) {
var group = DUMMY_KEYWORD;
var name = DUMMY_KEYWORD + "_" + 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 (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 = QUAT.multiply(MyAvatar.jointToWorldRotation(QUAT.IDENTITY(), 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 : QUAT.multiply(rotation, parentJoint.initialXform.rot);
joint.recoveryPosition = VEC3.sum(parentJoint.recoveryPosition, VEC3.multiplyQbyV(rotation, VEC3.multiply(joint.initialXform.pos, 0.01)));
parentJoint = joint;
}
};
this.update = function() {
if (!self.getActive()) {
return;
}
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 (!self.getActive()) {
return;
}
var i, index;
if (useCollisions) {
var handCollisions = handSystem.checkThreadCollisions(self);
var bodyCollisions = collisionSystem.checkThreadCollisions(self);
var handTouchedJoint = -1;
for (i = 0; i < self.joints.length; i++) {
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]);
}
}
} else {
for (i = 0; i < self.joints.length; i++) {
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 = VEC3.sum(pos0, VEC3.multiplyQbyV(joint0.initialXform.rot, VEC3.multiply(joint1.initialXform.pos, 0.01)));
var vec0 = VEC3.subtract(initial_pos1, pos0);
var vec1 = VEC3.subtract(pos1, pos0);
var delta = QUAT.rotationBetween(vec0, vec1);
joint0.currentRotation = QUAT.multiply(delta, joint0.initialXform.rot);
for (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(
QUAT.inverse(joint0.currentRotation),
VEC3.subtract(rootFramePositions[j], VEC3.multiply(joint0.initialXform.pos, 0.01))
);
}
pos0 = rootFramePositions[i];
pos1 = rootFramePositions[i+1];
initial_pos1 = VEC3.sum(pos0, VEC3.multiplyQbyV(joint1.initialXform.rot, VEC3.multiply(nextJoint.initialXform.pos, 0.01)));
vec0 = VEC3.subtract(initial_pos1, pos0);
vec1 = VEC3.subtract(pos1, pos0);
delta = QUAT.rotationBetween(vec0, vec1);
joint1.currentRotation = QUAT.multiply(delta, joint1.initialXform.rot);
joint0 = joint1;
joint1 = nextJoint;
}
};
this.apply = function() {
if (!self.getActive()) {
return;
}
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();
}
};
this.getActive = function() {
return flowJointData[self.joints[0]].node.active;
};
self.computeThread(root);
};
var isActive, flowSkeleton, flowJointData, flowThreads, handSystem, collisionSystem, collisionDebug, jointDebug;
function initFlow() {
stopFlow();
flowSkeleton = undefined;
flowJointData = [];
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();
Script.requestGarbageCollection();
MyAvatar.clearJointsData();
}
var setFlowScale = function(scale) {
collisionSystem.setScale(scale);
handSystem.setScale(scale);
for (var i = 0; i < flowThreads.length; i++) {
for (var j = 0; j < flowThreads[i].joints.length; j++){
var joint = flowJointData[flowThreads[i].joints[j]];
joint.node.radius = joint.node.initialRadius * scale;
}
}
};
var calculateConstraints = function() {
var collisionKeys = Object.keys(CUSTOM_COLLISION_DATA);
flowSkeleton = MyAvatar.getSkeleton().filter(
function(jointInfo){
var name = jointInfo.name;
var namesplit = name.split("_");
console.log("FLOW checking: " + name);
var isSimJoint = (name.substring(0, 3).toUpperCase() === SIM_JOINT_PREFIX.toUpperCase());
var isFlowJoint = (namesplit.length > 2 &&
namesplit[0].toUpperCase() === FLOW_JOINT_PREFIX.toUpperCase());
if (isFlowJoint || isSimJoint) {
var group = undefined;
if (isSimJoint) {
console.log("FLOW is sim: " + name);
for (var k = 1; k < name.length-1; k++) {
var subname = parseFloat(name.substring(name.length-k));
if (isNaN(subname) && name.length-k > SIM_JOINT_PREFIX.length) {
group = name.substring(SIM_JOINT_PREFIX.length, name.length - k + 1);
break;
}
}
if (group === undefined) {
group = name.substring(SIM_JOINT_PREFIX.length, name.length - 1);
}
} else {
group = namesplit[1];
}
if (group !== undefined) {
FLOW_JOINT_KEYWORDS.push(group);
var jointSettings;
if (CUSTOM_FLOW_DATA[group] !== undefined) {
jointSettings = CUSTOM_FLOW_DATA[group];
} else if (PRESET_FLOW_DATA[group] !== undefined){
jointSettings = PRESET_FLOW_DATA[group];
} else {
jointSettings = DEFAULT_JOINT_SETTINGS.get();
}
FLOW_JOINT_DATA[group] = jointSettings;
if (flowJointData[jointInfo.index] === undefined) {
flowJointData[jointInfo.index] = new FlowJoint(jointInfo.index, jointInfo.parentIndex, name, group, jointSettings);
}
}
}
else {
var collisionSettings = (collisionKeys.length > 0) ? CUSTOM_COLLISION_DATA[name] : PRESET_COLLISION_DATA[name];
if (collisionSettings) {
collisionSystem.addCollisionShape(jointInfo.index, name, collisionSettings);
}
}
return (isFlowJoint || isSimJoint);
}
);
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 (i = 0; i < roots.length; i++) {
var thread = new FlowThread(roots[i]);
// add threads with at least 2 joints
if (thread.joints.length > 0) {
if (thread.joints.length == 1) {
var jointIndex = roots[i];
var joint = flowJointData[jointIndex];
var jointPosition = MyAvatar.getJointPosition(jointIndex);
var settings = {
"active": joint.node.active,
"radius": joint.node.radius,
"gravity": joint.node.gravity,
"damping": joint.node.damping,
"inertia": joint.node.inertia,
"delta": joint.node.delta,
"stiffness": ISOLATED_JOINT_STIFFNESS
}
var extraIndex = flowJointData.length;
flowJointData[extraIndex] = new FlowJointDummy(jointPosition, extraIndex, jointIndex, -1, settings);
flowJointData[extraIndex].isDummy = false;
flowJointData[extraIndex].length = ISOLATED_JOINT_LENGTH;
flowJointData[jointIndex].childIndex = extraIndex;
flowJointData[extraIndex].group = flowJointData[jointIndex].group;
thread = new FlowThread(jointIndex);
}
flowThreads.push(thread);
}
}
if (flowThreads.length === 0) {
MyAvatar.clearJointsData();
}
if (SHOW_DUMMY_JOINTS) {
var jointCount = flowJointData.length;
var extraIndex = flowJointData.length;
var rightHandIndex = MyAvatar.getJointIndex("RightHand");
var rightHandPosition = MyAvatar.getJointPosition(rightHandIndex);
var parentIndex = rightHandIndex;
for (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, FLOW_JOINT_DATA[DUMMY_KEYWORD]);
parentIndex = extraIndex;
extraIndex++;
}
flowJointData[jointCount].node.anchored = true;
var extraThread = new FlowThread(jointCount);
flowThreads.push(extraThread);
}
setFlowScale(MyAvatar.scale);
};
Script.update.connect(function(){
if (!isActive || !flowSkeleton) return;
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);
MyAvatar.skeletonChanged.connect(function(){
Script.setTimeout(function() {
stopFlow();
MyAvatar.clearJointsData();
initFlow();
}, 200);
});
MyAvatar.scaleChanged.connect(function(){
if (isActive) {
setFlowScale(MyAvatar.scale);
}
});
var varsToDebug = {
"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);
}
},
"toggleDummyJoints": function() {
SHOW_DUMMY_JOINTS = !SHOW_DUMMY_JOINTS;
stopFlow();
initFlow();
},
"setJointDataValue": function(group, name, value) {
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 = (typeof value) != "boolean" ? parseFloat(value) : value;
FLOW_JOINT_DATA[group][name] = floatVal;
if (name === "stiffness") {
joint.stiffness = floatVal;
} else if (name === "radius") {
joint.node.initialRadius = floatVal;
joint.node.radius = MyAvatar.scale*floatVal;
}
else {
joint.node[name] = floatVal;
}
}
}
}
},
"setCollisionDataValue": function(group, name, value) {
var jointName = group;
var floatVal = parseFloat(value);
collisionSystem.modifyCollision(jointName, name, floatVal);
},
"getGroupData": function() {
var joint_filter_keys = FLOW_JOINT_DATA;
if (isActive) {
joint_filter_keys = {};
for (var i = 0; i < FLOW_JOINT_KEYWORDS.length; i++) {
joint_filter_keys[FLOW_JOINT_KEYWORDS[i]] = undefined;
}
for (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] = {
"active": joint.node.active,
"stiffness": joint.stiffness,
"radius": joint.node.initialRadius,
"gravity": joint.node.gravity,
"damping": joint.node.damping,
"inertia": joint.node.inertia,
"delta": joint.node.delta
};
}
}
}
}
return joint_filter_keys;
},
"getDisplayData": function() {
return {"avatar": SHOW_AVATAR, "collisions": USE_COLLISIONS, "debug": SHOW_DEBUG_SHAPES, "solid": SHOW_SOLID_SHAPES};
},
"getDefaultCollisionData": function() {
return DEFAULT_COLLISION_SETTINGS;
},
"getCollisionData": function() {
return collisionSystem.getCollisionData();
},
"addCollision": function(jointName) {
return collisionSystem.addCollisionToJoint(jointName);
},
"removeCollision": function(jointName) {
collisionSystem.removeCollisionFromJoint(jointName);
}
};
// 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);
if (typeof FLOWAPP === 'undefined') {
initFlow();
}