(function(){ var SHOW_DEBUG_SHAPES = true; var USE_COLLISIONS = true; var GRAVITY = 0.0096; var DELTA = 0.8; var DAMPING = 0.85; var JOINT_FILTER_KEYWORDS = ["hair", "skirt"]; var CONTRAINT_ITERATIONS = 1; var COLLISION_FRICTION = 0.1; 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_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.1, offset: {x: 0, y: 0.06, z: 0.05}}, "Spine2": {type: "box", dimensions: {x: 0.1, y: 0.03, z: 0.05}} //"LeftShoulder": {radius: 0.09, offset: {x: 0, y: 0.05, z: 0.00}}, //"RightShoulder": {radius: 0.09, offset: {x: 0, y: 0.05, z: 0.00}}, //"RightArm": {radius: 0.07, offset: {x: 0, y: 0.00, z: 0.00}}, //"LeftArm": {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.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(); } }); } this.checkCollisions = function(point) { var collision = new FlowCollisionData(); for (var i = 0; i < self.avatarHands.length; i++) { for (var j = 0; j < self.HANDS_COLLISION_JOINTS.length; j++) { var side = self.HANDS_COLLISION_JOINTS[j]; collision = self.avatarHands[i][side].checkCollision(point); if (collision.offset > 0) { break; } } if (collision.offset > 0) { varsToDebug.collision = collision; break; } } return collision; } } var handSystem = new FlowHandSystem(); var FlowDebug = function() { var self = this; this.debugLines = {}; this.debugSpheres = {}; this.debugCubes = {}; this.showDebugShapes = 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: 1, 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, visible: self.showDebugShapes }); } else if (self.showDebugShapes) { self.debugCubes[cubeName] = Overlays.addOverlay("cube", { position: position, rotation: rotation, dimensions: dimensions, color: color, 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, visible: self.showDebugShapes }); } else if (self.showDebugShapes){ self.debugSpheres[sphereName] = Overlays.addOverlay("sphere", { color: color, position: pos, scale: {x:scale, y:scale, z:scale}, visible: self.showDebugShapes }); } } this.cleanup = function() { for (lineName in self.debugLines) { Overlays.deleteOverlay(flowDebug.debugLines[lineName]); } for (sphereName in self.debugSpheres) { Overlays.deleteOverlay(flowDebug.debugSpheres[sphereName]); } } 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.addCollisionSphere = function(name, jointIndex, radius, offset) { self.collisionSpheres.push(new FlowCollisionSphere(name, jointIndex, radius, offset)); } this.update = function() { for (var i = 0; i < self.collisionSpheres.length; i++) { self.collisionSpheres[i].update(); } } this.computeCollision = function(collisions) { var collisionData = new FlowCollisionData(); collisionData.collisionCount = collisions.length; if (collisions.length > 0) { 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); } return collisionData; } this.checkCollisions = function(point) { var collisions = []; for (var i = 0; i < self.collisionSpheres.length; i++) { var collision = self.collisionSpheres[i].checkCollision(point); if (collision.offset > 0) { 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) { 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.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(self.rotation, self.jointIndex); } flowDebug.setDebugSphere(self.name, self.position, 2*self.radius, {red: 200, green: 10, blue: 50}); flowDebug.setDebugCube(self.name, self.position, self.rotation, self.dimensions, {red: 200, green: 10, blue: 50}); } this.checkCollision = function(point) { //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 new FlowCollisionData(); } } 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){ var self = this; this.index = index; this.name = name; this.parentIndex = parentIndex; this.initialPosition = MyAvatar.getJointPosition(index); this.initialRotation = MyAvatar.getJointRotation(index); this.initialTranslation = MyAvatar.getJointTranslation(index); this.oldPosition = this.initialPosition; 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.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 jointRotation = self.computeLookAtRotation(parentIndex, grandParentIndex, position); /* var jointRotation = self.getJointRotationToPoint(jointIndex, parentIndex, grandParentIndex, position); MyAvatar.setJointRotation(parentIndex, jointRotation.rotation); MyAvatar.setJointTranslation(jointIndex, jointRotation.translation); */ //MyAvatar.setJointTranslation(jointIndex, Vec3.multiply(self.translationDirection, 100*jointRotation.distance)); var parentRotation = MyAvatar.getJointRotation(parentIndex); var translation = scaleVec3(MyAvatar.worldToJointPoint(position, parentIndex), 100); MyAvatar.setJointRotation(parentIndex, parentRotation); MyAvatar.setJointTranslation(jointIndex, translation); } this.update = function (delta, damping, acceleration) { self.acceleration = acceleration; self.velocity = subtractVec3s(self.currentPosition, self.previousPosition); self.oldPosition = self.previousPosition self.previousPosition = self.currentPosition; if (!self.anchored) { self.currentPosition = addVec3s(addVec3s(self.currentPosition, scaleVec3(self.velocity, damping)), scaleVec3(self.acceleration, Math.pow(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.oldPosition, 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; for (var i = 0; i < JOINT_FILTER_KEYWORDS.length; i++) { isFlowJoint = isFlowJoint || name.indexOf(JOINT_FILTER_KEYWORDS[i]) > -1; } if (isFlowJoint){ if (flowJointData[jointInfo.index] === undefined) { flowJointData[jointInfo.index] = new FlowJoint(jointInfo.index, jointInfo.parentIndex, name); } } 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]; flowJoint.anchored = (parentFlowJoint === undefined); } for (var i = 0; i < flowSkeleton.length; i++) { var index = flowSkeleton[i].index; var flowJoint = flowJointData[index]; var parentFlowJoint = flowJointData[flowJoint.parentIndex]; flowJoint.anchored = (parentFlowJoint === undefined); 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"); } var acceleration = {x:0, y:-GRAVITY, z:0}; Script.beginProfileRange("JS-Update-JointsUpdate"); for (var i = 0; i < updateOrder.length; i++) { var index = updateOrder[i].index; var joint = flowJointData[index]; joint.update(DELTA, DAMPING, acceleration); } 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) : undefined; var handCollision = USE_COLLISIONS ? handSystem.checkCollisions(jointPosition) : 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; } }; // Register GlobalDebugger for API Debugger Script.registerValue("GlobalDebugger", varsToDebug); // Capture the controller values var leftTriggerPress = function (value) { grabPower.left = value; }; var rightTriggerPress = function (value) { grabPower.right = 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); })()